Глава 16. Работа с различными форматами данных
16.1. Механизм XDTO
16.1.1. Общая информация
Механизм XDTO является универсальным способом представления данных для взаимодействия с различными внешними источниками данных и программными системами.
Аббревиатура XDTO обозначает XML Data Transfer Objects.
Механизм XDTO позволяет создать модель представления данных (модель типов и значений), которая, с одной стороны, обеспечивает возможность просто и естественно манипулировать данными в среде системы «1С:Предприятие», а с другой стороны, хорошо приспособлена для прозрачного преобразования данных в другие форматы, главным образом XML.

Рис. 504. Общая схема XDTO
Можно выделить несколько задач, для решения которых используется механизм XDTO:
● обмен данными между конфигурациями системы «1С:Предприятие» с разными схемами данных;
● обмен данными на основе схем XML, не привязанных к той или иной конфигурации (например, обмен с информационными системами, построенными не на основе системы «1С:Предприятие»);
● организация работы с Web-сервисами. Механизм XDTO позволяет описывать типы параметров и возвращаемых значений Web-сервисов, а также манипулировать передаваемыми и возвращаемыми данными.
Механизм XDTO обладает следующими ключевыми свойствами:
● обеспечивает работу с XML,
● привычная модель работы с данными.
В настоящее время обмен данными с различными программными платформами и системами реализуется с использованием XML: XML-документы служат для представления данных, а схема XML используется для описания форматов и структур данных. Механизм XDTO позволяет создавать требуемые для обмена схемы XML и формировать XML-документы, удовлетворяющие этим схемам.
В то же время использование механизма XDTO позволяет выполнять эти действия в привычной, для большинства разработчиков системы «1С:Предприятие», манере.
Разработчик имеет дело с типами и объектами данных, объекты данных содержат свойства, свойствам присваиваются значения и т. д. При манипулировании данными с помощью XDTO разработчик максимально изолирован от подробностей, связанных с тем, как эти данные представлены в XML. Конечно, совсем избавиться от этих подробностей невозможно, но важно, что они проявляются только там, где это действительно нужно.
16.1.2. Фабрика XDTO
16.1.2.1. Общая информация
Ключевым понятием механизма XDTO является фабрика XDTO. Фабрика XDTO содержит описание всех типов, с которыми оперирует некоторая система. В частности, для любой конфигурации системы «1С:Предприятие» существует глобальная фабрика XDTO, которая описывает все типы, используемые в конфигурации, в терминах XDTO (эта фабрика XDTO доступна через свойство глобального контекста ФабрикаXDTO).
Все описания типов, которые содержит фабрика XDTO, сгруппированы в один или несколько пакетов XDTO. Если проводить аналогию между XDTO и XML, то можно сказать, что пакет XDTO соответствует схеме XML. Таким образом, фабрика XDTO может соответствовать нескольким схемам XML.
Фабрика XDTO является полностью самодостаточной. То есть любой из типов, зарегистрированных в фабрике XDTO, может ссылаться только на типы из той же самой фабрики XDTO.
В общем случае фабрика XDTO создается единовременно на основании описаний всех типов, которые должны быть зарегистрированы в фабрике. Для создания фабрики XDTO средствами встроенного языка используется конструктор объекта ФабрикаXDTO, которому передается набор схем XML, содержащийся в объекте НаборСхемXML. Сценарий, при котором типы XDTO добавляются в фабрику по одному или группами, не поддерживается.
В отличие от произвольной фабрики XDTO, которую может создать разработчик, глобальная фабрика XDTO создается системой автоматически, при создании новой информационной базы, и допускает добавление типов XDTO по одному или группами. Для этого используются средства визуального конструирования, позволяющие добавлять пакеты XDTO в ветку дерева метаданных Общие ‑ XDTO-пакеты. Все пакеты, содержащиеся в глобальной фабрике XDTO, можно разделить на три вида:
● Один пакет XDTO, содержащий описание типов платформы. Этот пакет является одинаковым для всех конфигураций системы «1С:Предприятие».
● Один пакет XDTO, содержащий описание типов конфигурации, созданных в результате редактирования метаданных (создания и изменения свойств справочников, документов и пр.).
● Один или несколько пакетов XDTO, описанных непосредственно в дереве объектов конфигурации, в ветке Общие ‑ XDTO-пакеты.
Пакет XDTO содержит описание некоторого множества типов, принадлежащих одному пространству имен ‑ пространству имен пакета. Кроме непосредственно описаний типов пакет XDTO может содержать ссылки на пакеты, которые используются данным пакетом, а также список определений глобальных свойств пакета.
Ссылки на другие пакеты содержатся в свойстве Зависимости пакета XDTO и представляют собой объект КоллекцияПакетовXDTO. Пакеты этой коллекции содержат типы из пространства имен, на которые имеются ссылки в данном пакете.
В пакете можно ссылаться на глобальные свойства из других пакетов.
Отсутствует поддержка циклов в директивах импорта и включения XSD-схем.
16.1.2.2. Получение фабрики XDTO из файла-схемы XSD
Ниже приведен пример создания фабрики XDTO на основе схемы XML, содержащейся в файле XML. Так как механизм XDTO представляет собой абстракцию, построенную «над» XML, то для получения схемы XML из файла XML необходимо последовательно «пройти» несколько уровней работы с данными XML:
● сначала низкоуровневое чтение/запись файлов XML;
● затем объектную модель XML, из которой уже может быть получен объект встроенного языка СхемаXML, содержащий данные схемы XML.

Рис. 505. Создание фабрики XDTO
Пример:
Копировать в буфер обмена// Создать фабрику XDTO на основе схемы XML, // содержащейся в файле XML // Создать объект чтения XML по умолчанию ЧтениеXML = Новый ЧтениеXML; // Открыть файл XML ЧтениеXML.ОткрытьФайл("с:\temp\my_sсhema.xsd"); // Создать построитель документа DOM по умолчанию ПостроительDOM = Новый ПостроительDOM; // Прочитать файл XML в документ DOM ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML); // Создать построитель схемы XML по умолчанию ПостроительСхемыXML = Новый ПостроительСхемXML; // Получить схему XML из документа DOM СхемаXML = ПостроительСхемыXML.СоздатьСхемуXML(ДокументDOM); // Создать набор схем XML по умолчанию НаборСхемXML = Новый НаборСхемXML; // Добавить схему XML в набор схем XML НаборСхемXML.Добавить(СхемаXML); // Создать фабрику XDTO на основе набора схем XML НоваяФабрикаXDTO = Новый ФабрикаXDTO(НаборСхемXML);
В приведенном примере сначала создается объект ЧтениеXML и открывается файл XML, расположенный на диске. После этого с помощью построителя документа DOM создается объект ДокументDOM, содержащий данные файла XML. Затем с помощью построителя схемы XML на основе документа DOM создается новый объект СхемаXML, содержащий данные схемы XML. В заключение создается пустой набор схем XML, в который добавляется имеющаяся схема XML, и на основании этого набора создается фабрика XDTO.
16.1.2.3. Получение фабрики XDTO из нескольких источников
В некоторых случаях может потребоваться создать фабрику на основании нескольких схем, некоторые из которых могут поставляться в виде файлов, а некоторые необходимо «унаследовать» от других фабрик XDTO (включая глобальную фабрику).
Копировать в буфер обменаСхемы = Новый Массив; Схемы.Добавить("с:\temp\sсhema 1.xsd"); Схемы.Добавить("с:\temp\sсhema 2.xsd"); Пакеты = Новый Массив; Пакеты.Добавить(ФабрикаXDTO.Пакеты.Получить("URI пакета 1")); Пакеты.Добавить(ФабрикаXDTO.Пакеты.Получить("URI пакета 2")); МояФабрика = СоздатьФабрикуXDTO(Схемы, Пакеты);
В данном примере выполняется формирование фабрики XDTO на основании 4 источников:
● Схемы XML, которая расположена в файле с:\temp\sсhema 1.xsd.
● Схемы XML, которая расположена в файле с:\temp\sсhema 2.xsd.
● Схемы пакета 1 текущей конфигурации.
● Схемы пакета 2 текущей конфигурации.
В общем случае, необязательно, чтобы пакеты получались из глобальной фабрики XDTO. Это может быть произвольная фабрика.
16.1.2.4. Получение фабрики XDTO из объекта данных XDTO
При формировании графа объектов XDTO, следует использовать один и тот же экземпляр фабрики XDTO. Использование значений или объектов XDTO, которые созданы различными фабриками, не допускается. Чтобы получить экземпляр фабрики XDTO какого-либо значения XDTO, следует использовать метод Фабрика() объектов ОбъектXDTO или ЗначениеXDTO.
Пример некорректного использования:
Копировать в буфер обменаURI = "http://v8.1c.ru/8.1/data/enterprise/current-config"; ТипСсылки = ФабрикаXDTO.Тип(URI, "CatalogRef.Контрагенты"); КонтрагентСсылка = ФабрикаXDTO.Создать(ТипСсылки, СсылкаНаЭлементСправочника); … ОпределениеСервиса = WSСсылки.WSСсылка1.ПолучитьWSОпределения(); ФабрикаСервиса = ОпределениеСервиса.ФабрикаXDTO; ТипОбъекта = ФабрикаСервиса.Тип(URI, "DocumentObject.Накладная"); ДокументОбъект = ФабрикаСервиса.Создать(ТипОбъекта); ДокументОбъект.Контрагент = КонтрагентСсылка; // ОШИБКА!
Пример корректного использования:
Копировать в буфер обменаURI = "http://v8.1c.ru/8.1/data/enterprise/current-config"; ОпределениеСервиса = WSСсылки.WSСсылка1.ПолучитьWSОпределения(); ТипСсылки = ОпределениеСервиса.ФабрикаXDTO.Тип(URI, "CatalogRef.Контрагент"); КонтрагентСсылка = ОпределениеСервиса.ФабрикаXDTO.Создать(ТипСсылки, СсылкаНаЭлементСправочника); … ФабрикаСсылки = КонтрагентСсылка.Фабрика(); ТипОбъекта = ФабрикаСсылки.Тип(URI, "DocumentObject.Накладная"); ДокументОбъект = ФабрикаСсылки.Создать(ТипОбъекта); ДокументОбъект.Контрагент = КонтрагентСсылка; // ПРАВИЛЬНО
В примере корректного использования, свойство ОпределениеСервиса.ФабрикаXDTO и функция КонтрагентСсылка.Фабрика() возвращают один и тот же экземпляр фабрики.
Смотри также:
● Значение XDTO (см. здесь).
● Объект XDTO (см. здесь).
16.1.3. Типы данных XDTO
16.1.3.1. Общая информация
Каждый из типов данных XDTO является либо типом значения XDTO, либо типом объекта XDTO. Соответственно, для описания типа значения используется объект ТипЗначенияXDTO, а для описания типа объекта ‑ ТипОбъектаXDTO.
Объект ТипЗначенияXDTO используется для описания типов простых неделимых значений, в которых не могут быть выделены отдельные составляющие. Примерами простых значений являются разнообразные строки, числа, даты и т. п.
Объект ТипОбъектаXDTO используется для описания типов экземпляров данных, имеющих некоторое состояние, представляемое как совокупность значений свойств этого экземпляра данных. При этом типы свойств этого экземпляра данных могут являться как типами значений XDTO, так и типами объектов XDTO.
И ТипЗначенияXDTO, и ТипОбъектаXDTO имеют два одинаковых свойства:
● Имя ‑ имя типа;
● URIПространстваИмен ‑ URI пространства имен, в котором определен данный тип.
Значения этих свойств совпадают с аналогичными параметрами, с которыми тип определяется в схеме XML. Имя типа и URI пространства имен образуют уникальный идентификатор типа. Имя типа должно быть обязательно определено. При этом свойство URIПространстваИмен может содержать пустую строку, хотя это и нежелательно.
16.1.3.2. Тип значения XDTO
Тип значения XDTO в соответствии с правилами для simple type из схемы XML может определяться тремя способами:
● ограничением, когда задается базовый тип (свойство БазовыйТип) и набор ограничений на множество возможных значений (свойство Фасеты);
● объединением, когда тип получается в результате объединения нескольких типов значений (объединяемые типы перечисляются в свойстве ТипыЧленовОбъединения);
● списком, когда значение представляет собой список значений (тип значения элементов, составляющих список значений, задается в свойстве ТипЭлементаСписка).
Помимо свойств Имя и URIПространстваИмен тип значения XDTO содержит следующие свойства:
● БазовыйТип ‑ базовый тип для данного типа значения XDTO. Базовые типы могут наследоваться, но только от других типов значений XDTO. Допустимое множество значений унаследованного типа представляет собой подмножество возможных значений базового типа. Верхним уровнем в иерархии простых типов является предопределенный тип anySimpleType из пространства имен http://www.w3.org/2001/XMLSchema. Все типы значений прямо или опосредованно унаследованы от этого типа. Типы, образованные объединением или списком, всегда непосредственно унаследованы от anySimpleType.
● Фасеты ‑ список фасетов (ограничений), ограничивающих множество допустимых значений по отношению к базовому типу. Список фасетов задается только для типов значений XDTO, определенных ограничением базового типа. Каждый отдельный фасет представляет собой пару: имя фасета и значение. Определен список имен допустимых фасетов. Причем не любой из допустимых фасетов может быть применен к любому типу. Список фасетов и применимость их к тому или иному типу определяются по правилам XML Schema (http://www.w3.org/TR/xmlschema-2/).
● ТипыЧленовОбъединения ‑ список типов, образующих объединение. Объединяться могут только типы значений XDTO. Если тип образован объединением, то список ТипыЧленовОбъединения содержит по крайней мере один тип. При этом список Фасеты должен быть пустым, а свойство ТипЭлементаСписка должно возвращать неопределенное значение.
● ТипЭлементаСписка ‑ в случае, когда тип значения XDTO определяется списком, данное свойство показывает тип элемента списка. При этом списки Фасеты и ТипыЧленовОбъединения должны быть пустыми.
● Список имен допустимых фасетов (определяется системным перечислением ВидФасетаXDTO):
● Длина ‑ фасет длины. Содержит количество единиц длины, причем единица длины имеет различный смысл для различных типов. Для типов string и anyURI длина содержит количество символов. Для типов hexBinary и base64Binary длина содержит количество байт двоичных данных. Для типов, определяемых списком, длина содержит количество элементов списка.
● МаксВключающее ‑ фасет максимума, включающего границу. Ограничивает пространство значений данного типа максимальным значением. Любое значение данного типа меньше либо равно указанному значению.
● МаксДлина ‑ фасет максимальной длины. Содержит максимальное количество единиц длины, причем единица длины имеет различный смысл для различных типов. Для типа string максимальная длина содержит максимальное количество символов. Для типов hexBinary и base64Binary максимальная длина содержит максимальное количество байт двоичных данных. Для типов, определяемых списком, максимальная длина содержит максимальное количество элементов списка.
● МаксИсключающее ‑ фасет максимума, не включающего границу. Ограничивает пространство значений данного типа максимальным значением. Любое значение данного типа меньше указанного значения.
● МинВключающее ‑ фасет минимума, включающего границу. Ограничивает пространство значений данного типа минимальным значением. Любое значение данного типа больше либо равно указанному значению.
● МинДлина ‑ фасет минимальной длины. Содержит минимальное количество единиц длины, причем единица длины имеет различный смысл для различных типов. Для типа string минимальная длина содержит минимальное количество символов. Для типов hexBinary и base64Binary минимальная длина содержит минимальное количество байт двоичных данных. Для типов, определяемых списком, минимальная длина содержит минимальное количество элементов списка.
● МинИсключающее ‑ фасет минимума, не включающего границу. Ограничивает пространство значений данного типа минимальным значением. Любое значение данного типа больше указанного значения.
● Образец ‑ фасет образца. Содержит регулярное выражение, определяющее пространство значений данного типа.
● Перечисление ‑ фасет перечисления. Определяет набор допустимых значений данного типа.
● ПробельныеСимволы ‑ фасет пробельных символов. Может принимать одно из трех значений:
● Сохранять ‑ строка может содержать любые пробельные символы.
● Заменять ‑ строка не должна содержать #x9 (табуляция), #xA (перевод строки) и #xD (возврат каретки). Если они существуют, то должны быть заменены символом #x20 (пробел).
● Сворачивать ‑ дополнительно к требованиям, указанным для значения replace, строка не должна содержать парных символов #x20 (пробел), а также лидирующих и завершающих символов #x20 (пробел).
● РазрядовВсего ‑ фасет общего количества цифр. Содержит общее количество разрядов числа (целая часть плюс дробная часть).
● РазрядовДробнойЧасти ‑ фасет количества цифр дробной части. Содержит количество разрядов дробной части числа.
Инфраструктура XDTO определяет набор предопределенных типов значений XDTO. Этот набор совпадает с набором примитивных типов, определенных в XML Schema Part 2: Datatypes. Предопределенные типы образуют иерархию в соответствии с XML Schema Part 2: Datatypes. Имена типов совпадают с именами типов XML Schema и принадлежат URI пространства имен ‑ http://www.w3.org/2001/XMLSchema. Предопределенные типы являются автоматически зарегистрированными в любой фабрике XDTO.
16.1.3.3. Тип объекта XDTO
Помимо свойств Имя и URIПространстваИмен тип объекта XDTO содержит следующие свойства:
● БазовыйТип ‑ базовый тип для данного типа. Это может быть только тип объекта XDTO. Базовым в иерархии типов объектов XDTO является предопределенный тип anyType из пространства имен http://www.w3.org/2001/XMLSchema. Все типы объектов XDTO прямо или опосредованно унаследованы от этого типа.
● Открытый ‑ признак того, является ли тип объекта XDTO открытым. Данное свойство показывает, может ли экземпляр объекта XDTO содержать дополнительные свойства, не определенные в его типе, то есть реализует модель open content. Соответствует появлению в XML-схеме для данного типа описаний: <anyAttribute>, <any>.
● Абстрактный ‑ признак того, является ли тип объекта XDTO абстрактным. Соответствует появлению в схеме для данного элемента атрибута abstract="true".
● Упорядоченный ‑ признак того, является ли порядок следования элементов, представляющих значения свойств, строго соответствующим порядку следования свойств в типе объекта XDTO. Если задана модель контента xsd:all, то порядок следования элементов XML может быть произвольным. При этом допустимым является порядок, соответствующий порядку следования свойств в типе. То есть если свойство Упорядоченный имеет значение Ложь, то на входе порядок следования элементов XML не контролируется, а на выходе определяется порядком следования свойств, если только признак Последовательный не имеет значение Истина.
● Последовательный ‑ это свойство показывает, содержит ли экземпляр соответствующего объекта XDTO последовательность XDTO. Данный признак равен значению Истина в тех случаях, когда порядок следования вложенных элементов XML не может однозначно определяться порядком следования свойств в типе (например, в схеме XML контент задан как <sequence … maxOccurs=10 … >) или для соответствующего типа XML в схеме определен атрибут mixed="true". Последовательность XDTO позволяет задать в явном виде порядок следования элементов, как они будут представлены в документе XML. Для объектов типов, у которых свойство Последовательный установлено в значение Ложь, порядок следования вложенных элементов соответствует порядку следования свойств.
● Смешанный ‑ свойство показывает, определен ли в схеме XML для данного типа mixed content. Если значение свойства Смешанный равно Истина, то значение Последовательный обязательно равно Истина, так как mixed content невозможно смоделировать без применения последовательности XDTO.
● Свойства ‑ список свойств, определенных для данного типа объекта XDTO. Каждое из свойств представляется в виде экземпляра объекта СвойствоXDTO. Список содержит полный список свойств, в том числе свойства, определенные в базовом типе.
Существует предопределенный тип объекта XDTO с именем anyType и URI пространства имен http://www.w3.org/2001/XMLSchema. Данный тип является базовым для любого типа объекта XDTO, но у него нет базового типа. Он является открытым, не абстрактным, подразумевает наличие последовательности и имеет пустой список свойств.
Данный тип объекта XDTO соответствует типу anyType, определенному в XML Schema Part 2: Datatypes.
16.1.3.4. Свойство XDTO
Отдельно взятое свойство отдельного типа объекта XDTO описывается с помощью экземпляра объекта СвойствоXDTO. Это означает, что один и тот же экземпляр объекта СвойствоXDTO не может быть использован для описания свойств в различных типах объектов XDTO и двух различных свойств одного типа объекта XDTO.
Объект СвойствоXDTO содержит следующие свойства:
● Имя ‑ имя свойства. В пределах одного типа объекта XDTO имена свойств должны быть уникальными.
При формировании модели данных XDTO на основе схемы XSD, имена свойств XDTO образуются на основании имен атрибутов и элементов, описанных в схеме. Построение модели типа выполняется последовательно: сначала формируется список свойств на основе атрибутов, затем ‑ на основе элементов, в порядке объявления в схеме.
При этом имя приводится в соответствие с правилами именования, принятыми во встроенном языке. Символы, допустимые в имени XML (например: ".", "-«'), но недопустимые с точки зрения имени для встроенного языка ‑ заменяются на символ "_". В случае дублирования имени атрибута и элемента, дубликату назначается имя, расширенное числовым суффиксом (начиная от 1).
● Тип ‑ тип свойства. Может быть как экземпляром объекта ТипЗначенияXDTO, так и экземпляром объекта ТипОбъектаXDTO.
● ВерхняяГраница ‑ свойство типа объекта XDTO может быть определено как содержащее одно или множество значений. Свойство считается содержащим одно значение, если равно 1. Если же свойство ВерхняяГраница больше 1, то считается, что оно может содержать множество значений. Такое свойство в структуре объекта моделируется как список (не путать со списком в описании типа значения XDTO). Свойство ВерхняяГраница показывает максимальное количество значений свойства. Значение больше 1 может быть задано только для свойств, представляемых в виде элемента XML. Свойство ВерхняяГраница соответствует атрибуту xsd:maxOccurs в XML Schema. Значение ‑1 соответствует unbounded.
● НижняяГраница ‑ минимальное количество значений свойства. Минимальное количество значений свойства может принимать значения меньше или равно 0. Естественно, значение НижняяГраница должно быть меньше или равно значению ВерхняяГраница (если, конечно, ВерхняяГраница не равно ‑1);
● ВозможноПустое ‑ показывает, может ли свойство принимать неопределенное значение. Неопределенное значение свойства представляется в XML в виде элемента следующего вида: <elem xsi:nil="true" />. Таким образом, свойство ВозможноПустое, равное Истина, может быть определено только для свойств с формой представления Элемент. Свойство ВозможноПустое соответствует атрибуту xsd:nillable в XML Schema. Если значение свойства ВерхняяГраница больше 1, неопределенное значение является допустимым для элемента списка значений свойства.
● ЗначениеПоУмолчанию ‑ значение свойства по умолчанию. Значением свойства по умолчанию может быть только ЗначениеXDTO. При этом данное значение должно быть того же типа, что и тип свойства или же унаследованного типа. При создании объекта XDTO свойство, если оно допускает единственное значение, принимает значение по умолчанию (метод Установлено() объекта XDTO возвращает значение Ложь для этого свойства). Для свойств с множеством значений список значений изначально пуст, независимо от того, определено или нет значение по умолчанию.
● Фиксированное ‑ указывает, является ли значение свойства фиксированным. Если установлено в значение Истина, то само фиксированное значение можно получить через свойство ЗначениеПоУмолчанию.
● Форма ‑ форма представления свойства в XML. Это может быть Текст, Элемент или Атрибут. Если формой представления является Атрибут или Текст, то значение свойства ВерхняяГраница не может быть больше 1. Если свойство принимает значение Текст, то значение свойства НижняяГраница также должно быть равным 1. У одного типа только одно свойство может иметь форму представления Текст, при этом остальные свойства должны иметь форму представления Атрибут.
● ЛокальноеИмя ‑ локальное имя атрибута или элемента, используемого для представления свойства. Для свойств с формой представления Текст ‑ пустая строка.
● URIПространстваИмен ‑ URI пространства имен для атрибута или элемента, используемого для представления свойства. Пустая строка, если пространство имен отсутствует.
16.1.4. Экземпляры данных XDTO
Экземпляры данных XDTO могут являться значениями XDTO (ЗначениеXDTO) или объектами XDTO (ОбъектXDTO).
16.1.4.1. Значение XDTO
Значение XDTO представляет собой простое неделимое значение, в котором не могут быть выделены отдельные составляющие. Примерами простых значений являются разнообразные строки, числа, даты и т. п. Экземпляры простых значений являются немутабельными.
Новое значение XDTO может быть создано с помощью метода Создать() фабрики XDTO:
● на основе типа значения XDTO и значения;
● на основе типа значения XDTO и лексического представления значения.
Ниже приведены примеры создания значения XDTO.
Пример:
Копировать в буфер обменаГлобальнаяФабрикаXDTO = ФабрикаXDTO; // Создать значение XDTO из ссылки СсылкаНаЭлементСправочника = Справочники.Номенклатура.НайтиПоКоду("0000001"); ТипЗначенияXDTOСоздаваемогоЗначения = ГлобальнаяФабрикаXDTO.Тип("urn:schemas v8 1c ru:config data", "CatalogRef.Номенклатура"); НовоеЗначениеXDTO = ГлобальнаяФабрикаXDTO.Создать(ТипЗначенияXDTOСоздаваемогоЗначения, СсылкаНаЭлементСправочника); // Создать значение XDTO из лексического представления значения ТипЗначенияXDTOСоздаваемогоЗначения = ГлобальнаяФабрикаXDTO.Тип("http://www.w3.org/2001/XMLSchema", "dateTime"); НовоеЗначениеXDTO = ГлобальнаяФабрикаXDTO.Создать(ТипЗначенияXDTOСоздаваемогоЗначения, "2006-04-20T12:00:30");
Новое значение XDTO может быть получено также путем чтения файла XML.
Пример:
Копировать в буфер обменаГлобальнаяФабрикаXDTO = ФабрикаXDTO;, // Прочитать данные значения XDTO из файла XML НовоеЧтениеXML = Новый ЧтениеXML; НовоеЧтениеXML.ОткрытьФайл("D:/Exchange.xml"); … НовоеЗначениеXDTO = ГлобальнаяФабрикаXDTO.ПрочитатьXML(НовоеЧтениеXML); Значение XDTO может быть записано в файл XML. ГлобальнаяФабрикаXDTO = ФабрикаXDTO; // Записать данные значения XDTO в файл XML НоваяЗаписьXML = Новый ЗаписьXML; НоваяЗаписьXML.ОткрытьФайл("D:/Exchange.xml"); … ГлобальнаяФабрикаXDTO.ЗаписатьXML(НоваяЗаписьXML, НовоеЗначениеXDTO);
16.1.4.2. Объект XDTO
В противовес простому значению состояние объекта XDTO представляется как совокупность значений его свойств. Экземпляры объекта XDTO являются мутабельными, то есть во время жизни объекта XDTO его состояние может быть изменено путем изменения значений отдельных его свойств. В качестве значений свойств могут фигурировать любые экземпляры данных XDTO, как значение XDTO, так и объект XDTO. Когда значением свойства является объект XDTO, говорят, что значением свойства является ссылка на объект.
Новый объект XDTO может быть создан с помощью метода Создать() фабрики XDTO, на основе типа объекта XDTO. После этого следует присвоить соответствующие значения свойствам объекта XDTO. Ниже приведен пример создания объекта XDTO и заполнения его свойств.
Пример:
Копировать в буфер обменаГлобальнаяФабрикаXDTO = ФабрикаXDTO; // Создать "пустой" объект XDTO ТипОбъектаXDTOСоздаваемогоОбъекта = ГлобальнаяФабрикаXDTO.Тип("http://www.1c.ru/demos/products", "Номенклатура"); НовыйОбъектХDTO = ГлобальнаяФабрикаXDTO.Создать(ТипОбъектаXDTOСоздаваемогоОбъекта); // Заполнить значения свойств объекта XDTO ОбъектСправочника = СсылкаНаЭлементСправочника.ПолучитьОбъект(); НовыйОбъектХDTO.Наименование = ОбъектСправочника.Наименование; НовыйОбъектХDTO.ПолноеНаименование = ОбъектСправочника.ПолноеНаименование; НовыйОбъектХDTO.ЗакупочнаяЦена = ОбъектСправочника.ЗакупочнаяЦена; НовыйОбъектХDTO.ШтрихКод = ОбъектСправочника.ШтрихКод;
Так же как и значение XDTO, данные объекта XDTO могут быть прочитаны из файла XML или записаны в файл XML.
Пример:
Копировать в буфер обменаГлобальнаяФабрикаXDTO = ФабрикаXDTO;, // Прочитать данные объекта XDTO из файла XML НовоеЧтениеXML = Новый ЧтениеXML; НовоеЧтениеXML.ОткрытьФайл("D:/Exchange.xml"); … НовыйОбъектXDTO = ГлобальнаяФабрикаXDTO.ПрочитатьXML(НовоеЧтениеXML); … // Записать данные объекта XDTO в файл XML НоваяЗаписьXML = Новый ЗаписьXML; НоваяЗаписьXML.ОткрытьФайл("D:/Exchange.xml"); … ГлобальнаяФабрикаXDTO.ЗаписатьXML(НоваяЗаписьXML,НовыйОбъектХDTO);
При чтении нетипизированных данных, считываемый элемент, в случае наличия атрибутов или дочерних элементов, считывается в ОбъектXDTO типа xsd:anyType. Данный тип является открытым типом со смешанным содержимым, поэтому текст в элементе интерпретируется не как значение свойства __content, а как текст и помещается в последовательность объекта.
Например, если выполняет чтение элемента вида <element attr="attr_value">element value</element>, то текст element value можно получить следующим образом ОбъектXDTO.Последовательность().ПолучитьТекст(0). Выражение XPath, описывающее требуемое свойство, оперирует данными только внутри текущего объекта.
Объект ОбъектXDTO содержит следующие методы:
● Тип() ‑ возвращает тип данного объекта XDTO (ТипОбъектаXDTO);
● Установить(Выражение, Значение), Установить(Свойство, Значение) ‑ позволяет установить значение свойства:
● Выражение ‑ выражение на XPath, указывающее свойство;
● Свойство ‑ свойство, представленное значением типа объект СвойствоXDTO;
● Значение -устанавливаемое значение свойства.
Свойство, с которым работают методы объекта XDTO, могут быть заданы объектом типа СвойствоXDTO или выражением XPath. Если свойство задано некорректно или устанавливаемое значение не может быть присвоено свойству (например, тип несовместим с типом свойства), то вызывается исключение. Если свойству присваивается неопределенное значение, а свойство ВозможноПустое равно Ложь, то выдается исключение. Если свойству присваивается ссылка на объект XDTO и ссылка на этот объект XDTO уже является значением какого-либо другого свойства, то данная ссылка перестает быть значением этого другого свойства.
Цепочки ссылок на объекты XDTO, содержащиеся в свойствах объектов, не могут образовывать циклов. Поэтому при присваивании ссылки на объект XDTO, вызывающий образование цикла, вызывается исключение. Если свойство допускает множество значений, то для него применение метода Установить() недопустимо и приводит к вызову исключения. При присваивании значения свойству производится проверка на допустимость присваивания данного типа значения свойству. Значение может быть присвоено в том случае, если его тип совпадает с типом свойства, является унаследованным от типа свойства или является одним из типов, входящих в объединение. При присваивании, если формой представления значения свойства в XML является Текст или Атрибут, производится приведение значения к типу свойства. Если формой представления является Элемент, значение присваивается как есть.
● Получить(<Свойство>), Получить(<Выражение>) ‑ получение значения свойства:
● Свойство ‑ свойство, представленное значением типа объект СвойствоXDTO;
● Выражение ‑ выражение на XPath, указывающее свойство. Для свойств с множеством значений данный метод возвращает список значений свойства ‑ СписокXDTO. Все операции модификации значений свойства должны выполняться через этот список.
● Сбросить(<Свойство>), Сбросить (<Выражение>) ‑ сброс значения свойства:
● Свойство ‑ свойство, представленное значением типа объект СвойствоXDTO;
● Выражение ‑ выражение на XPath, указывающее свойство. Действие метода Сбросить() для различных свойств различно. Для свойств, допускающих множество значений (ВерхняяГраница > 1), выполнение метода Сбросить() приводит к очистке списка значений.
● Добавить(Форма, URIПространстваИмен, ЛокальноеИмя, ЭлементДанных, НазначениеТипа), Добавить(Имя, ЭлементДанных, НазначениеТипа) ‑ позволяет добавить значение свойства, которое отсутствует в описании типа. Данная возможность доступна, если тип определен как тип с открытой моделью содержания:
● Форма ‑ тип добавляемого свойства (ФормаXML);
● URIПространстваИмен ‑ идентификатор пространства имен добавляемого свойства;
● ЛокальноеИмя ‑ локальное имя добавляемого свойства;
● Имя ‑ полное имя добавляемого свойства;
● ЭлементДанных ‑ элемент данных, который будет установлен в вкачестве значения доабвляемого свойства (ЗначениеXDTO, ОбъектXDTO);
● НазначениеТипа ‑ определяет способ описания типа доабвляемого свойства при сериализации в XML/JSON (НазначениеТипаXML). Если параметр установлен в значение НазначениеТипа.Явное, то при сериализации будет записываться атрибут xsi:type.
● Установлено() ‑ проверяет, установлено ли значение свойства. Непосредственно после создания объекта у всех свойств результатом выполнения метода Установлено() будет значение Ложь.
● Последовательность() ‑ возвращает объект-последовательность (ПоследовательностьXDTO), принадлежащий данному объекту XDTO. С помощью последовательности XDTO также можно модифицировать состояние объекта. Данный метод возвращает последовательность XDTO только в том случае, если у типа объекта установлено свойство Последовательный.
● Проверить() ‑ данный метод позволяет проверить правильность заполнения значений свойств объекта XDTO. При проверке проверяются также объекты, ссылки на которые являются значениями свойств. Предметом проверки является соответствие количества значений свойств свойствам НижняяГраница и ВерхняяГраница, правильность следования значений свойств в последовательности XDTO, если свойство Упорядоченный имеет значение Истина. Проверка прекращается при нахождении первой же ошибки. При этом выдается исключение.
16.1.4.3. Последовательность XDTO
С помощью объекта ПоследовательностьXDTO моделируется порядок следования элементов и фрагментов текста, как они выглядят в XML-представлении объекта. Последовательность состоит из пар «свойство ‑ значение». В качестве свойств могут выступать только свойства с формой представления Элемент, так как порядок следования атрибутов не важен. Свойство в паре «свойство ‑ значение» может также иметь неопределенное значение. В этом случае считается, что данный элемент последовательности представляет фрагмент текста. Появление элементов последовательности, представляющих фрагменты текста, допустимо только для объектов типов, у которых значение свойства Смешанный равно Истина.
При формировании содержимого объекта XDTO с помощью присваивания значений свойствам порядок присваивания отражается в последовательности XDTO.
Последовательность XDTO содержит следующие методы:
● Количество() ‑ возвращает число элементов последовательности.
● ПолучитьСвойство(<Индекс>) ‑ возвращает свойство, которому соответствует значение, находящееся по индексу Индекс. Если Индекс находится за границами допустимых значений, выдается исключение. Метод может вернуть неопределенное значение, если элементу последовательности соответствует фрагмент текста из смешанного содержания (текст и элементы).
● ПолучитьЗначение(<Индекс>) ‑ возвращает значение, находящееся по индексу Индекс. Если Индекс находится за границами допустимых значений, выдается исключение.
● УстановитьЗначение(<Индекс>, <Элемент>) ‑ устанавливает значение Элемент по индексу Индекс. Индекс должен иметь значение в диапазоне допустимых индексов. Элемент должен иметь допустимое значение для свойства, для которого он устанавливается, или для текста.
● Добавить(<Свойство>, <Элемент>) ‑ добавляет пару «свойство ‑ значение» к последовательности. Значение должно быть допустимым для свойства.
● Добавить(<Текст>) ‑ добавляет фрагмент текста к последовательности. Если у типа объекта свойство Смешанный имеет значение Ложь, то выдается исключение.
● Вставить(<Индекс>, <Свойство>, <Элемент>) ‑ вставляет пару «свойство ‑ значение» в позицию Индекс последовательности. Индекс должен иметь значение внутри диапазона индексов. Элемент в позиции Индекс и все элементы с большими значениями индекса сдвигаются вправо на одну позицию.
● Вставить(<Индекс>, <Текст>) ‑ вставляет текст Текст в позицию Индекс последовательности. Индекс должен иметь значение внутри диапазона индексов. Элемент в позиции Индекс и все элементы с большими значениями индекса сдвигаются вправо на одну позицию.
● Удалить(<Индекс>) ‑ удаляет элемент последовательности в позиции Индекс. Индекс должен иметь значение внутри диапазона допустимых.
16.1.4.4. Список XDTO
С помощью объекта СписокXDTO моделируется список значений для свойств с множественными значениями (ВерхняяГраница > 1). Список представляет собой упорядоченный набор объектов, которые могут являться как значениями XDTO, так и объектами XDTO. Среди них могут иметь место неопределенные значения, если свойство ВозможноПустое имеет значение Истина. Но понятие «установленности» для элемента списка не определено.
Объект СписокXDTO содержит следующие методы:
● Количество() ‑ возвращает размер списка.
● Получить(<Индекс>) ‑ получает значение, находящееся по индексу Индекс. Индекс должен находиться в диапазоне допустимых. В противном случае выдается исключение.
● Установить(<Индекс>, <Элемент>) ‑ устанавливает значение Элемент в позицию Индекс. Устанавливаемое значение замещает ранее присутствовавшее значение. Индекс должен находиться в диапазоне допустимых, а Элемент должен быть допустимым для свойства. В противном случае выдается исключение.
● Добавить(<Элемент>) ‑ добавляет значение в хвост списка. Элемент должен быть допустимым для свойства. В противном случае выдается исключение.
● Вставить(<Индекс>, <Элемент>) ‑ внесение значения Элемент в позицию Индекс. Индекс должен находиться в диапазоне допустимых, а Элемент должен быть допустимым для свойства. В противном случае выдается исключение. Значение в позиции Индекс и значения с большими позициями сдвигаются вправо на одну позицию.
● Удалить(<Индекс>) ‑ удаление значения в позиции Индекс. Индекс должен находиться в диапазоне допустимых. Значения с большими позициями сдвигаются на освободившееся место.
16.1.4.5. XРath
Для навигации по дереву объектов могут использоваться выражения на XPath. Строго говоря, это не совсем XPath, а скорее несколько модифицированное подмножество XPath.
Основной конструкцией данного языка является путь к значению, который состоит из отдельных шагов. Шаги в пути отделяются друг от друга символами «/» (слеш). В качестве шага пути выступает имя свойства или предопределенные конструкции «.» (точка) и «..» (две точки).
Выражение приведенного ниже вида обозначает свойство с именем ИмяСвойства текущего объекта, а именно объекта, у которого вызвали метод Получить() или Установить().
Копировать в буфер обменаИмяСвойства
Выражение приведенного ниже вида означает, что у текущего объекта получено значение свойства ИмяСвойства1, а у объекта, ссылка на который является значением свойства ИмяСвойства1, получено свойство ИмяСвойства2.
Копировать в буфер обменаИмяСвойства1/ИмяСвойства2
Соответственно, шаг, обозначенный как точка, означает текущий объект, а две точки ‑ объект-владелец текущего.
Если путь поиска начинается с символа слеш, то это означает поиск от корня дерева объектов. Если какое-либо свойство в пути не найдено, то это вызывает исключение. Если в пути встречается свойство с множественным значением, то результатом является весь список значений данного свойства.
Например, если в пути, приведенном ниже, свойство Список имеет множественное значение, то результат данного выражения ‑ это список (СписокXDTO) значений данного свойства.
Копировать в буфер обменаСвойство/Список
Для того чтобы получить отдельное значение из этого списка, нужно через точку от имени свойства указать 0-базированный индекс значения в списке, как это показано ниже.
Копировать в буфер обменаСвойство/Список.0
Индекс должен быть задан как целое число, находящееся в пределах диапазона допустимых индексов. В противном случае будет вызвано исключение.
Можно получить отдельное значение из списка с использованием 1-базированного индекса. Для этого применяется конструкция следующего вида:
Копировать в буфер обменаСвойство/Список[1]
Индекс может принимать значения от 1 до числа элементов в списке.
Имеется также возможность поиска в списке (только для объектов). Выражение для поиска выглядит следующим образом:
Копировать в буфер обменаСвойство/Список[ИмяСвойства='СтрокаПоиска']
Здесь Список ‑ это свойство с множественным значением. Значением списка являются объекты, у которых имеется свойство с именем ИмяСвойства. Результатом будет первый объект в списке, значение свойства Свойство которого равно строковому значению ‘СтрокаПоиска’. Если ни одного объекта не найдено, то результатом является неопределенное значение. Свойство ИмяСвойства также может присутствовать не у всех объектов в списке. А может и не присутствовать ни у кого. Значение, с которым сравнивается значение свойства, может быть задано в виде числа, логического значения (True или False) или строкового литерала.
Копировать в буфер обменаСвойство/Список[ИмяСвойства!='СтрокаПоиска']
Приведенное выше выражение аналогично предыдущему примеру, за исключением того, что результатом будет первый объект в списке, значение свойства ИмяСвойства которого не равно строковому значению ‘СтрокаПоиска’.
Ниже приведено определение описываемого подмножества XPath.
<Путь>
Копировать в буфер обмена[/] <Список шагов>
<Список шагов>
Копировать в буфер обмена<Шаг> [/<Список шагов>] |
<Шаг>
Копировать в буфер обмена<Имя свойства> [<Уточнение>] | .. | . |
<Имя свойства>
Копировать в буфер обмена[<Буква> | _]<Остаток имени>
<Остаток имени>
Копировать в буфер обмена{<Буква> | <Цифра> | _} <Остаток имени> |
<Уточнение>
Копировать в буфер обмена.<0 базированный индекс> | [<Имя свойства>=<Значение>] | [<Имя свойства>!=<Значение>] | [<1 базированный индекс>]
<0-базированный индекс>
Копировать в буфер обмена<Целое без знака>
<1-базированный индекс>
Копировать в буфер обмена<Целое без знака>
<Целое без знака>
Копировать в буфер обмена<Цифра> <Цифры>
<Цифра>
Копировать в буфер обмена0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
<Цифры>
Копировать в буфер обмена<Цифра> <Цифры> |
<Значение>
Копировать в буфер обмена<Число> | <Строка> | <Булево>
<Число>
Копировать в буфер обмена[+| ]<Целое без знака>[.<Целое без знака>]
<Строка>
Копировать в буфер обмена"<Символы>" | '<Символы>'
<Булево>
Копировать в буфер обменаtrue | false
Примечание. В строке с ограничителями «"» среди символов не может встречаться «"». Аналогично в строке с разделителями «’» не может встречаться символ «’».
При сравнении значения свойства со значением, заданным в виде литерала, значение, заданное в виде литерала, приводится к типу свойства по правилам приведения, после чего производится сравнение.
16.1.5. XML-сериализация на основе XDTO
Значения типов конфигураций системы «1С:Предприятие» могут быть сериализованы непосредственно в(из) файл(а) XML на основе XDTO.
Для этого используется объект СериализаторXDTO, который может быть получен с помощью конструктора на основе существующей фабрики XDTO. Работа с объектом СериализаторXDTO аналогична работе с глобальными процедурами и функциями работы с XML.
Например, сериализация ссылки на справочник Номенклатура в файл XML может быть выполнена с помощью программного кода.
Пример:
Копировать в буфер обмена// Получить ссылку на элемент справочника Номенклатура СсылкаНаЭлементСправочника = Справочники.Номенклатура.НайтиПоКоду("0000001"); // Создать сериализатор XDTO для глобальной фабрики XDTO НовыйСериализаторXDTO = Новый СериализаторXDTO(ФабрикаXDTO); // Создать объект записи XML и открыть файл НоваяЗаписьXML = Новый ЗаписьXML; НоваяЗаписьXML.ОткрытьФайл("D:/Exchange.xml"); // ... // Cериализовать ссылку в XML НовыйСериализаторXDTO.ЗаписатьXML(НоваяЗаписьXML, СсылкаНаЭлементСправочника, НазначениеТипаXML.Явное);
Ниже приведен пример сериализации ссылки на справочник Номенклатура из файла XML.
Пример:
Копировать в буфер обмена// Создать сериализатор XDTO для глобальной фабрики XDTO НовыйСериализаторXDTO = Новый СериализаторXDTO(ФабрикаXDTO); // Прочитать данные объекта XDTO из файла XML НовоеЧтениеXML = Новый ЧтениеXML; НовоеЧтениеXML.ОткрытьФайл("D:/Exchange.xml"); … // Сериализовать ссылку из XML НоваяСсылкаНаСправочник = НовыйСериализаторXDTO.ПрочитатьXML(НовоеЧтениеXML);
Кроме сериализации единичного значения данных (в примере выше ‑ элемент справочника Номенклатура), с помощью сериализатора XDTO можно сериализовать сразу все доступные объекты какого-либо вида метаданных. Например, для XDTO-сериализации всего справочника Номенклатура, можно будет использовать следующий пример:
Копировать в буфер обменаЗаписьXML.ЗаписатьНачалоЭлемента("root"); СериализаторXDTO.ЗаписатьXML(ЗаписьXML, Метаданные.Справочники.Номенклатура, НазначениеТипаXML.Явное); ЗаписьXML.ЗаписатьКонецЭлемента();
Перечень объектов, которые могут быть сериализованы путем указания соответствующего объекта метаданных, см. здесь.
16.1.6. Рекомендации по оформлению схем XML
Преобразование НаборСхемXML à ФабрикаXDTO à НаборСхемXML в общем случае не дает на выходе набор схем XML, эквивалентный исходному. Однако следование набору рекомендаций по оформлению схем XML позволит добиться эквивалентности выходного набора схем исходному, а именно:
● для преобразования НаборСхемXML à ФабрикаXDTO à НаборСхемXML будет соблюдаться эквивалентность выходного набора схем исходному;
● успешная проверка правильности заполнения свойств объекта ОбъектXDTO (метод Проверить()) гарантирует, что представление объекта в XML будет соответствовать схеме XML;
● обеспечивается максимальная гибкость и отсутствие искажений при использовании полиморфизма.
Схема XML, полученная на основе фабрики XDTO, для типов которой не переопределялись значения по умолчанию параметров, ответственных за XML-представление данных, безусловно, соответствует приведенным рекомендациям.
В общем, приведенный далее список рекомендаций представляет собой набор правил, позволяющий добиться наилучших результатов с различных точек зрения.
Схема XML не должна содержать анонимных типов
Недопустимы конструкции следующего вида:
Копировать в буфер обмена<element name="Person">
<complexType>
<sequence>
<element name="FirstName" type="string" />
<element name="FamilyName" type="string" />
</sequence>
</complexType>
</element>
Этот фрагмент нужно оформить следующим образом:
Копировать в буфер обмена<element name="Person" type="tns:PersonType">
<complexType name="PersonType">
<sequence>
<element name="FirstName" type="string" />
<element name="FamilyName" type="string" />
</sequence>
</complexType>
</element>
Для контента сложных типов следует использовать только модель sequence
Для моделирования контента у сложных типов (complexType) следует использовать только единственный блок sequence без переопределения значений по умолчанию атрибутов minOccurs и maxOccurs.
Копировать в буфер обмена<complexType name="PersonType">
<sequence>
<element name="FirstName" type="string" />
<element name="FamilyName" type="string" />
</sequence>
</complexType>
Модель all не нарушает тождественность исходной и результирующей схемы при преобразовании НаборСхемXML à ФабрикаXDTO à НаборСхемXML, однако имеет ряд ограничений. В частности, значение атрибута maxOccurs для элементов в модели all не может быть больше 1.
Модель choice нарушает эквивалентность исходной и результирующей схем и не позволяет с помощью метода Проверить() проверить соответствие заполнения данных объекта схеме XML.
По тем же причинам не следует использовать в рамках одного сложного типа комбинацию из нескольких моделей контента или же определять для sequence значения атрибутов minOccurs и maxOccurs, отличные от значений по умолчанию.
Желательно свойства объектов представлять в XML в виде элементов
Представление свойств как атрибутов XML не влияет на эквивалентность исходной и результирующей схем. Однако имеется ряд ограничений:
● В атрибуте не может быть представлено значение объектного типа ‑ только типов-значений.
● В атрибуте не могут быть представлены свойства с множественными значениями.
● В случае полиморфных типов у свойства, представленного как атрибут XML, может происходить искажение типа значения. А именно: при присваивании значения свойству тип значения приводится точно к типу свойства, так как только для значения свойства в элементе XML можно указать атрибут xsi:type, который позволяет точно указать тип значения свойства.
Как следствие не нужно использовать simpleContent для complexType, так как эта модель для хранения значений подразумевает использование свойств атрибутов и текста включающего элемента XML.
Не рекомендуется использовать одинаковые имена для объявления атрибута и элемента. Правила формирования имени свойства XDTO см. здесь.
Не следует использовать модель mixed content
Применение в схеме XML конструкции приведенного ниже вида означает, что в элементе XML, соответствующем описываемому типу, текст может быть перемешан с элементами XML.
Копировать в буфер обмена<complexType name="FormLetter" mixed="true"> </complexType>
Для поддержки такого информационного содержимого у соответствующего объекта ТипОбъектаXDTO значения свойств Последовательный и Смешанный установлены в значение Истина, а у каждого экземпляра соответствующего объекта ОбъектXDTO появляется последовательность XDTO (объект ПоследовательностьXDTO). Управление информационным содержимым таких объектов намного сложнее, чем у объектов, состояние которых представлено только набором значений свойств.
К счастью, в подавляющем большинстве случаев в применении mixed content не возникает необходимости.
Свойство ФормаЭлементовПоУмолчанию у схемы XML должно иметь значение Квалифицированная.
Данная рекомендация является элементом хорошего стиля, и XDTO придерживается этого стиля.
16.1.7. Правила проверки фабрики XDTO
Общая схема кодирования идентификаторов сообщений об ошибках проверки фабрики XDTO выглядит следующим образом:
Копировать в буфер обменаxdto <область> <раздел>[ <правило>]: <описание ошибки>
● <область> ‑ область проверки (фабрика XDTO, пакет XDTO, тип значения XDTO, фасет XDTO, тип объекта XDTO);
● <раздел> ‑ номер раздела, проверка правила которого завершилась неудачей;
● <правило> ‑ правило раздела;
● <описание ошибки> ‑ описание ошибки.
16.1.7.1. Правила проверки непосредственно фабрики XDTO
При проверке непосредственно фабрики XDTO ошибки кодируются префиксом model. При этом общий префикс будет иметь следующий вид:
Копировать в буфер обменаxdto model <раздел>[ <правило>]: <описание ошибки>
Пакеты, входящие в модель, должны иметь уникальные URI пространства имен ‑ дублирование пакетов в рамках модели запрещено.
Директива импорта должна определять непустое URI пространства имен импортируемого пакета.
Директивы импорта пакетов должны определять существующие пакеты типов.
Пакеты типов, определенные в модели, должны удовлетворять правилам проверки пакетов.
16.1.7.2. Правила проверки пакета XDTO
При проверке правильности пакета XDTO ошибки кодируются префиксом package. При этом общий префикс будет иметь следующий вид:
Копировать в буфер обменаxdto package <раздел>[ <правило>]: <описание ошибки>
Пакет XDTO должен иметь установленное свойство URIПространстваИмен.
Типы, определяемые в пакете XDTO, могут иметь ссылки только на типы, указанные в списке импортируемых (свойство Зависимости).
Директивы импорта должны удовлетворять следующим правилам:
● директивы импорта должны определять пакеты XDTO, в которых не могут содержаться директивы импорта, указывающие на данный пакет XDTO, ‑ зацикливание импортируемых директив не допускается;
● директива импорта должна определять не пустое свойство URIПространстваИмен импортируемого пакета XDTO;
● директивы импорта должны определять существующие пакеты XDTO.
Свойства пакета должны удовлетворять следующим правилам:
● имена свойств пакета должны быть установлены и не являться пустыми;
● имена свойств пакета должны быть уникальны в пределах пакета;
● тип свойства пакета должен быть установлен или определен;
● типы свойств пакета должны быть определены в пакете или его зависимостях;
● задание одновременно имени типа глобального свойства и анонимного определения типа глобального свойства недопустимо;
● свойство пакета не должно ссылаться на определение другого свойства пакета;
● свойство пакета не должно определять границы количества значений свойства;
● свойство пакета может иметь форму представления только Атрибут или Элемент.
16.1.7.3. Правила проверки типа значения XDTO
При проверке правильности типа значения XDTO ошибки кодируются префиксом valueType. При этом общий префикс будет иметь следующий вид:
Копировать в буфер обменаxdto valueType <раздел>[ <правило>]: <описание ошибки>
● Общие правила проверки типа значения XDTO:
● если тип определен в рамках пакета типов, то должны выполняться следующие условия:
● тип значения XDTO должен иметь установленное свойство Имя, содержащее непустое имя;
● имя типа значения XDTO должно быть уникальным в пределах пакета XDTO (среди всех типов пакета XDTO);
● если тип определен в рамках другого определения типа значения или в рамках определения свойства объектного типа, то должно выполняться следующее условие:
● свойство Имя определения типа не должно быть установлено;
● тип значения XDTO не может содержать ссылок на самого себя ни в базовом типе, ни в типе элемента списка, ни в одном из типов объединения на всю глубину иерархии.
● Правила проверки базового типа ‑ свойство БазовыйТип:
● если свойство БазовыйТип не установлено:
● если свойство Вариант не установлено, а свойство ТипыЧленовОбъединения не установлено, или свойство Вариант установлено и имеет значение Атомарный:
● если свойство ОпределениеТипа содержит единственное значение, то это определение типа является определением анонимного базового типа;
● в противном случае базовым типом считается тип anySimpleType пространства имен XML-схемы (http://www.w3.org/2001/XMLSchema);
● если свойство БазовыйТип определения типа установлено, то должны выполняться следующие условия:
● базовый тип должен удовлетворять второму правилу проверки пакета XDTO;
● базовый тип должен являться типом значения XDTO;
● базовый тип не может являться данным типом значения XDTO.
● Правила проверки типа элемента списка ‑ свойство ТипЭлементаСписка:
● если свойство ТипЭлементаСписка определения типа установлено, то должны выполняться следующие условия:
● тип элемента списка должен удовлетворять второму правилу проверки пакета XDTO;
● тип элемента списка должен являться типом значения XDTO;
● тип элемента списка не может являться данным типом значения XDTO;
● тип значения XDTO, являющийся элементом списка, должен быть либо атомарным, либо объединением типов значений XDTO, состоящим только из атомарных типов значений XDTO;
● если свойство Вариант не установлено, а свойство БазовыйТип установлено или свойство Вариант установлено и имеет значение Список:
● если свойство ОпределениеТипа содержит единственное значение, то это определение типа является определением анонимного типа элемента списка;
● в противном случае значение свойства ТипЭлементаСписка определяется из соответствующего свойства базового типа значения XDTO.
● Правила проверки типа объединения ‑ свойство ТипыЧленовОбъединения:
● если свойство ТипыЧленовОбъединения определения типа XDTO установлено, то должны выполняться следующие условия:
● тип объединения должен удовлетворять второму правилу проверки пакета XDTO;
● тип объединения должен являться типом значения XDTO;
● тип объединения не должен являться данным типом значения XDTO;
● тип значения XDTO, являющийся объединением, должен быть либо атомарным, либо списком;
● если свойство Вариант не установлено, а свойство БазовыйТип установлено или свойство Вариант установлено и имеет значение Объединение:
● свойство ОпределениеТипа содержит значения, являющиеся анонимными определениями типов объединения;
● в противном случае значение свойства ТипыЧленовОбъединения определяется значением свойства ТипыЧленовОбъединения базового типа значения XDTO.
● Наследование типов значений XDTO считается правильным, если выполнятся следующие условия:
● для атомарных типов значений XDTO (после выполнения правил проверки типа элемента списка и правил проверки типа объединения свойства ТипЭлементаСписка и ТипыЧленовОбъединения не установлены) должны выполняться следующие условия:
● тип значения XDTO, являющийся базовым, должен быть атомарным, т. е. свойства ТипЭлементаСписка и ТипыЧленовОбъединения базового типа XDTO не должны быть установлены;
● предком типа значения XDTO должен быть один из примитивных типов пространства имен схемы XML (http://www.w3.org/2001/XMLSchema);
● состав фасетов, установленных в описании типа значения XDTO, должен соответствовать списку допустимых фасетов для примитивного типа, являющегося предком данного типа XDTO;
● значение каждого фасета, установленного в типе значения XDTO, должно удовлетворять правилам ограничения эффективного значения аналогичного фасета базового типа XDTO;
● для типов элемента списка (после выполнения правил проверки типа элемента списка и правил проверки типа объединения установлено свойство ТипЭлементаСписка):
● базовый тип XDTO не может являться типом объединения, т. е. свойство ТипыЧленовОбъединения базового типа XDTO не должно быть установлено;
● если свойство ТипЭлементаСписка базового типа XDTO установлено, то значение этого свойства должно определять базовый тип для типа элемента списка, определенного в свойстве ТипЭлементаСписка данного типа значения XDTO;
● если базовым типом является тип anySimpleType пространства имен XML схемы (http://www.w3.org/2001/XMLSchema), то среди списка фасетов допускается только фасет ПробельныеСимволы;
● в противном случае могут быть определены только фасеты: Образец, Перечисление, Длина, МинДлина, МаксДлина и ПробельныеСимволы;
● значение каждого фасета, установленного в типе, должно удовлетворять правилам ограничения эффективного значения аналогичного фасета базового типа;
● для типов объединения (после выполнения правил проверки типа элемента списка и правил проверки типа объединения установлено свойство ТипыЧленовОбъединения):
● базовым типом может являться тип anySimpleType пространства имен XML схемы (http://www.w3.org/2001/XMLSchema) или тип объединения, т. е. у базового типа должно быть установлено свойство ТипыЧленовОбъединения;
● если базовым типом является тип объединения, должны выполняться следующие условия:
● количество типов объединения базового типа не должно быть больше количества типов объединения данного типа;
● типы объединения должны быть потомками соответствующих им типов объединения базового типа в порядке следования в списке типов объединения;
● если базовым типом является тип anySimpleType пространства имен XML схемы (http://www.w3.org/2001/XMLSchema), то определение фасетов не допускается;
● в противном случае могут быть определены только фасеты Образец и Перечисление;
● значение каждого фасета, установленного в типе, должно удовлетворять правилам ограничения эффективного значения аналогичного фасета базового типа.
● Если свойство Вариант (модель содержания) определения типа установлено, то оно не должно противоречить определению типа:
● если свойство имеет значение Атомарный, то тип имеет атомарную модель содержания и должен удовлетворять правилам xdto-valueType-5.1;
● если свойство имеет значение Список, то тип имеет модель содержания Список и должен удовлетворять правилам xdto-valueType-5.2;
● если свойство имеет значение Объединение, то тип имеет модель содержания Объединение и должен удовлетворять правилам xdto-valueType-5.3.
16.1.7.4. Правила проверки типа объекта XDTO
При проверке правильности типа объекта XDTO ошибки кодируются префиксом objectType. При этом общий префикс будет иметь следующий вид:
Копировать в буфер обменаxdto objectType <раздел>[ <правило>]: <описание ошибки>
● Общие правила проверки типа объекта XDTO:
● если тип определен в рамках пакета типов, то должны соблюдаться следующие условия:
● тип объекта XDTO должен иметь установленное свойство Имя, содержащее непустое имя;
● имя типа объекта XDTO должно быть уникальным в пределах пакета (среди всех типов пакета);
● если тип определен в рамках свойства объектного типа, то должны соблюдаться следующие условия:
● свойство Имя определения типа не должно быть установлено.
● Правила проверки базового типа ‑ свойство БазовыйТип:
● если свойство БазовыйТип не установлено, то базовым типом считается тип anyType пространства имен XML схемы (http://www.w3.org/2001/XMLSchema);
● если свойство БазовыйТип типа объекта XDTO установлено, то должны выполняться следующие условия:
● базовый тип должен удовлетворять второму правилу проверки пакета XDTO;
● базовый тип должен являться типом объекта XDTO;
● базовый тип не может являться данным типом объекта XDTO.
● Каждое свойство типа объекта XDTO должно удовлетворять следующим правилам:
● имя свойства должно быть определено;
● имя свойства не может быть пустым;
● имя свойства должно быть уникальным для типа объекта XDTO;
● если свойство Тип установлено, то должны выполняться следующие условия:
● имя типа должно определять существующий тип объекта XDTO или тип значения XDTO;
● тип свойства должен удовлетворять второму правилу проверки пакета XDTO;
● свойство не может содержать определение анонимного типа;
● если свойство Тип не установлено, то должны выполняться следующие условия:
● если у свойства имеется определение анонимного типа, то типом свойства является тип, соответствующий данному определению;
● в противном случае типом свойства считается тип anyType пространства имен XML схемы (http://www.w3.org/2001/XMLSchema);
● если у свойства определено значение по умолчанию, то должны быть выполнены следующие условия:
● тип свойства ЗначениеПоУмолчанию должен быть типом значения XDTO;
● лексическое представление значения по умолчанию должно соответствовать пространству значений типа свойства XDTO;
● удовлетворять следующим требованиям:
● если свойство ЛокальноеИмя свойства XDTO не установлено, то в качестве локального имени XML представления свойства XDTO используется свойство Имя свойства XDTO;
● если свойство URIПространстваИмен свойства XDTO не установлено, то URI пространства имен XML представления свойства XDTO определяется следующим образом:
● если формой XML представления свойства XDTO (свойство Форма) является Элемент, то используется URI пространства имен типа, которому принадлежит данное свойство;
● в противном случае URI пространства имен XML-представления свойства XDTO считается отсутствующим;
● свойство XDTO должно быть уникально по XML-представлению в пределах типа объекта XDTO;
● если свойство XDTO имеет форму XML-представления Текст, то должны выполняться следующие правила:
● имя и URI пространства имен должны быть неустановленными или быть пустыми;
● среди свойств типа объекта XDTO допускается наличие свойств XDTO с формой XML представления Атрибут;
● если свойство XDTO имеет форму XML представления Элемент, то наличие среди свойств типа объекта XDTO свойства XDTO с формой XML-представления Текст запрещено;
● значение нижней границы количества появления значений свойства НижняяГраница может принимать значения неотрицательных целых чисел. Значение нижней границы НижняяГраница должно быть меньше или равно значению верхней границы количества появления значений свойств ВерхняяГраница при условии, что данное значение не равно ‑1;
● значение верхней границы количества значений свойства ВерхняяГраница может принимать значения неотрицательных целых чисел или ‑1. Если данное значение равно ‑1, то это означает неограниченное количество значений свойства;
● если значение свойства Фиксированное установлено, то по умолчанию должно быть установлено ЗначениеПоУмолчанию, соответствующее пространству значений типа свойства XDTO;
● если установлена ссылка на определение глобального свойства, то должны выполняться следующие правила:
● определение свойства не может переопределять значения свойств глобального определения;
● глобальное свойство, на определение которого ссылается данное определение свойства, должно быть определено в рамках данного пакета или пакетов-зависимостей.
● Если среди свойств типа объекта XDTO имеется свойство XDTO, совпадающее по имени или XML-представлению со свойством базового типа, то вид наследования определяется как наследование ограничением. При таком виде наследования должны выполняться следующие условия:
● для каждого свойства XDTO должны выполняться следующие условия:
● в базовом типе должно быть определено свойство XDTO с тем же именем ‑ переопределяемое свойство;
● если базовый тип определяет порядок следования свойств (свойство Упорядоченный), то позиция переопределяемого свойства должна быть идентична позиции свойства в типе-наследнике;
● форма XML представления переопределяемого свойства и свойства данного типа должна совпадать;
● локальное имя XML представления переопределяемого свойства и свойства данного типа должно совпадать;
● URI пространства имен XML представления переопределяемого свойства и свойства данного типа должна совпадать;
● если переопределяемое свойство определяет фиксированное значение, то:
● наличие фиксированного значения не может быть отменено в типе-наследнике;
● фиксированное значение в базовом типе и типе-наследнике должно совпадать;
● нижняя граница количества значений свойства должна быть меньше или равна нижней границе количества значений переопределяемого свойства;
● верхняя граница количества значений свойства должна быть больше или равна верхней границе количества значений переопределяемого свойства;
● тип свойства должен являться потомком типу переопределяемого свойства;
● если базовый тип не обладает смешанным содержанием (свойство Смешанный), то смешанная модель не может быть установлена в типе-наследнике;
● если порядок следования свойств базового типа фиксирован (свойство Упорядоченный), то порядок не может быть изменен в типе-наследнике;
● если базовый тип не определяет наличие последовательности (свойство Последовательный), то наличие последовательности не может быть установлено в типе-наследнике;
● если базовый тип не определяет открытую модель содержания (свойство Открытый), то открытая модель содержания не может быть установлена в типе-наследнике.
● В противном случае вид наследования определяется как наследование расширением. Для данного вида наследования должны выполняться следующие правила:
● если модель содержания базового типа является смешанной (свойство Смешанный), то она не может быть изменена в типе-наследнике;
● если порядок следования свойств базового типа не фиксирован (свойство Упорядоченный), то порядок не может быть изменен в типе-наследнике;
● если базовый тип определяет наличие последовательности (свойство Последовательный), то последовательность не может быть запрещена в типе-наследнике;
● если базовый тип определяет открытую модель содержания (свойство Открытый), то модель содержания не может быть изменена в типе-наследнике.
● При любом виде наследования должны выполняться следующие условия:
● если модель содержания является смешанной (свойство Смешанный), то наличие последовательности (свойство Последовательный) не может быть запрещено;
● если модель содержания является открытой (свойство Открытый), то наличие последовательности (свойство Последовательный) не может быть запрещено.
16.1.7.5. Правила ограничения фасетов
При проверке правильности фасета XDTO ошибки кодируются префиксом facet. При этом общий префикс будет иметь следующий вид:
Копировать в буфер обменаxdto facet <раздел>[ <правило>]: <описание ошибки>
Правила для фасета Длина:
● значение фасета должно совпадать с эффективным значением фасета базового типа;
● если установлено значение фасета МинДлина, то оно должно удовлетворять следующим условиям:
● значение фасета МинДлина должно быть меньше или равно значению фасета Длина;
● в базовом типе не установлено значение фасета Длина, или значение фасета МинДлина не отличается от эффективного значения фасета МинДлина базового типа;
● если установлено значение фасета МаксДлина, то оно должно удовлетворять следующим условиям:
● значение фасета МаксДлина должно быть меньше или равно значению фасета Длина;
● в базовом типе не установлено значение фасета Длина, или значение фасета МаксДлина не отличается от эффективного значения фасета МаксДлина базового типа.
Правила для фасета МинДлина:
● значение фасета должно быть больше или равно эффективному значению фасета в базовом типе;
● значение фасета должно быть меньше или равно эффективному значению фасета МаксДлина.
Правила для фасета МаксДлина:
● значение фасета должно быть меньше или равно эффективному значению фасета в базовом типе;
● значение фасета должно быть больше или равно эффективному значению фасета МинДлина.
Правила для фасета ПробельныеСимволы:
● если эффективное значение фасета базового типа равно collapse, то значение фасета не может принимать другое значение;
● если эффективное значение фасета базового типа равно preserve, то значение фасета не может принимать значение replace.
Правила для фасета МинВключающее:
● значение фасета должно быть меньше эффективного значения фасета МаксИсключающее данного типа;
● значение фасета должно быть больше или равно эффективному значению фасета базового типа;
● если установлено значение фасета МаксВключающее, то оно должно быть меньше или равно эффективному значению фасета МаксВключающее базового типа.
● если установлено значение фасета МинИсключающее, то оно должно быть больше эффективного значения фасета МинИсключающее базового типа;
● если установлено значение фасета МаксИсключающее, то оно должно быть меньше эффективного значения фасета МаксИсключающее базового типа.
Правила для фасета МинИсключающее:
● установка значения фасета МинИсключающее и МинВключающее фасета в определении типа не допускается;
● если установлено значение фасета МаксВключающее, то значение фасета МинИсключающее должно быть меньше значения фасета МаксВключающее данного типа;
● значение фасета должно быть больше или равно эффективному значению фасета базового типа;
● если установлено значение фасета МаксВключающее, то оно должно быть меньше или равно эффективному значению фасета МаксВключающее базового типа;
● если установлено значение фасета МинВключающее, то оно должно быть больше или равно эффективному значению фасета МинВключающее базового типа;
● если установлено значение фасета МаксИсключающее, то оно должно быть меньше эффективного значения фасета МинВключающее базового типа.
Правила для фасета МаксВключающее:
● значение фасета должно быть больше или равно значению фасета МинВключающее данного типа;
● если установлено значение фасета МаксИсключающее, то оно должно быть меньше эффективного значения базового типа;
● если установлено значение фасета МинВключающее, то оно должно быть больше или равно эффективному значению базового типа;
● если установлено значение фасета МинИсключающее, то оно должно быть больше эффективного значения базового типа.
Правила для фасета МаксИсключающее:
● установка значения фасета МаксИсключающее и МаксВключающее фасета в определении типа не допускается;
● если установлено значение фасета МинИсключающее, то значение фасета МаксИсключающее должно быть больше значения фасета МинИсключающее данного типа;
● значение фасета должно быть меньше или равно эффективному значению фасета базового типа;
● если установлено значение фасета МаксВключающее, то оно должно быть меньше или равно эффективному значению базового типа;
● если установлено значение фасета МинИсключающее, то оно должно быть больше эффективного значения базового типа;
● если установлено значение фасета МинВключающее, то оно должно быть больше эффективного значения базового типа.
Правило для фасета РазрядовВсего:
● значение фасета должно быть меньше или равно эффективному значению фасета базового типа.
Правила для фасета РазрядовДробнойЧасти:
● значение фасета должно быть меньше или равно эффективному значению фасета РазрядовВсего;
● значение фасета должно быть меньше или равно эффективному значению фасета базового типа.
16.2. Работа с JSON
16.2.1. Общая информация
Формат JSON (JavaScript Object Notation) является универсальным способом представления при обмене данными (RFC 7159, https://tools.ietf.org/html/rfc7159). Данные в формате JSON представляют собой (в закодированном виде):
● Объект ‑ неупорядоченное множество пар ключ:значение, заключенный в фигурные скобки ({}). Пары ключ:значение разделяются запятыми (,).
● Массив ‑ множество значений. Массив заключается в квадратные скобки ([]). Значения разделяются запятыми (,).
● Значение ‑ может быть строкой, числом, объектом, массивов или литералом true, false, null.
● Строка ‑ набор символов, заключенный в двойные кавычки ("").
● Число ‑ сериализуется с разделителем точка (.). Точность числа не ограничена.
Таким образом, с помощью вышеперечисленных элементов допускается описание объектов любой сложности для представления в формате JSON. Например, некоторый код на встроенном языке, формирует некоторый набор данных (в структуре):
Копировать в буфер обменаДанные = Новый Структура; Данные.Вставить("Фамилия", "Иванов"); Данные.Вставить("Имя", "Иван"); Данные.Вставить("Отчество", "Иванович"); Данные.Вставить("ДеньРождения", ДатаРождения); Телефоны = Новый Массив; Телефоны.Добавить("+7-987-123-45-67"); Телефоны.Добавить("+7-986-987-65-43"); Данные.Вставить("Телефоны", Телефоны);
Сериализуя этот объект в JSON, может быть получен следующий документ:
Копировать в буфер обмена{
"Фамилия": "Иванов",
"Имя": "Иван",
"Отчество": "Иванович",
"ДатаРождения": "2009-02-15T00:00:00Z",
"Телефоны": [
"+7-987-123-45-67",
"+7-986-987-65-43"
]
}
При работе с форматом JSON с помощью системы «1С:Предприятие», поддерживается сериализация (и десериализация) следующих типов системы «1С:Предприятие»:
|
Тип «1С:Предприятия» |
Тип JSON |
|
Строка |
Строка |
|
Число |
Число |
|
Булево |
Литералы true и false |
|
Дата |
Результат определяется настройками и методами, которые используются для работы с JSON. Подробнее написано далее. |
|
Неопределено |
Null |
|
Массив ФиксированныйМассив |
Массив JSON при условии, что любой элемент массива может быть сериализован в JSON |
|
Структура ФиксированнаяСтруктура |
Объект JSON, где: ● Ключ ‑ ключ элемента структуры. ● Значение ‑ значение элемента структуры в том случае, если оно может быть сериаловано в JSON |
|
Соответствие ФиксированноеСоответствие |
Объект JSON, где: ● Ключ ‑ ключ элемента структуры. Ключ может быть только значением типа Строка, в противном случае будет генерироваться исключение. ● Значение ‑ значение элемента структуры в том случае, если оно может быть сериаловано в JSON |
Если выполняется попытка сериализации типа, отсутствующего в таблице ‑ будет вызвано исключение.
JSON не стандартизует формат представления даты. В силу этого представление даты в JSON-документе определяется предпочтениями прикладного разработчика, который формирует документ, и требованиями системы, которая будет обрабатывать JSON-документ. Простая техника работы с JSON не предоставляет встроенных инструментов преобразования даты и требуемый формат. При работе с объектной и потоковыми техниками ‑ система «1С:Предприятие» позволяет указать несколько форматов даты для автоматического преобразования типа Дата при записи. Поддерживаются следующие форматы представления даты (задается с помощью системного перечисления ФорматДатыJSON):
1. Формат ISO (значение ФорматДатыJSON.ISO). В этом случае дата сериализуется следующим образом: "2009-02-15T00:00:00+0400".
2. Формат JavaScript (значение ФорматДатыJSON.JavaScript). В этом случае дата сериализуется следующим образом: "new Date(1234656000000)".
3. Формат Microsoft (значение ФорматДатыJSON.Microsoft). В этом случае дата сериализуется следующим образом: "\/Date(1234656000000)\/" или "/Date(1234656000000)/" (в зависимости от режима экранирования символов).
Дата может записываться несколькими вариантами (для примера используется дата 10 мая 2014 13:14:15 в зоне UTC+4):
● как локальная дата: 2014-05-10T13:14:15.
● как локальная дата с указанием смещения: 2014-05-10T13:14:15+04:00.
● как дата в UTC: 2014-05-10T09:14:15Z.
Управлять этим можно с помощью системного перечисления ВариантЗаписиДатыJSON. Дату в варианте UTC можно записать в любом формате (ISO, JavaScript и Microsoft), остальные варианты представления даты возможны только в том случае, если сериализация выполняется в формате ISO.
При записи JSON-документа предоставляются возможности по управлению формируемыми данными: настраивать перенос строк, формат сериализации даты, обработка символа "/" и т. д. Эти настройки можно выполнять с помощью объектов НастройкиСериализацииJSON и ПараметрыЗаписиJSON.
Работа с данными в формате JSON может выполняться в нескольких техниках:
● Простые операции ‑ позволяют преобразовать в формат JSON и обратно одно значение. Позволяет максимально гибко использовать формат JSON для различных операций взаимодействия с внешними системами. Более подробно описание простых операций работы с JSON см. здесь.
● Объектная техника ‑ позволяет формировать простые и небольшие JSON-документы для обмена с внешними приложениями или веб-сервисами. Структура JSON-документа автоматически формируется системой «1С:Предприятие» во время записи документа. Более подробно описание данной техники см. здесь. Специальным вариантом такой техники является возможность сериализации в формате JSON для XDTO-объектов системы «1С:Предприятие». Более подробное описание работы с XDTO-объектами см. здесь.
● Потоковая техника ‑ позволяет работать с данными большого объема без загрузки их в память приложения. Навигация по JSON-документу полностью ложится на прикладного разработчика (как при записи, так и при чтении документа). Более подробно описание данной техники см. здесь.
● Совмещенная техника ‑ позволяет сочетать гибкость потоковой техники и простоту объектной техники. Более подробно описание данной техники см. здесь.
16.2.2. Простая техника работы
Достаточно часто формат JSON используется при обмене данными через различные HTTP-сервисы. При таком сценарии необходимо быстро выполнять преобразования данных в формат JSON (при вызове какого-либо HTTP-сервиса) и обратно (при анализе ответа какого-либо HTTP-сервиса). Для выполнения таких операций встроенный язык предоставляет два метода:
● ЗаписатьЗначениеJSON() ‑ выполняет операцию сериализации значения встроенного языка в строку, которая содержит представление значения в формате JSON.
● ПрочитатьЗначениеJSON() ‑ выполняет десериализацию значения, которое передано в качестве входного параметра в виде строки в формате JSON.
Указанные выше методы позволяют преобразовывать в формат JSON (и обратно) все типы данных, которые перечислены в предыдущем разделе, за одним исключением: значения типа Дата не могут быть обработаны данными методами. Как было сказано выше, причиной такого поведения является то, что JSON не стандартизирует представление для значения типа Дата. Но прикладной разработчик знает, в каком формате используемый HTTP-сервис обрабатывает значения даты и времени. Значит он может самостоятельно преобразовать дату и время в нужный формат. Обратное утверждение также верно ‑ преобразование даты и времени из ответа HTTP-сервиса в значение типа Дата также лежит на прикладном разработчике.
Использование методов достаточно простое. Далее приведены примеры использования методов сериализации и десериализации различных значений в/из формата JSON.
Копировать в буфер обмена// *** Тип Число СтрокаJSON = ЗаписатьЗначениеJSON(20220805); ЗначениеJSON = ПрочитатьЗначениеJSON(СтрокаJSON); // *** Тип Строка СтрокаJSON = ЗаписатьЗначениеJSON("Привет, Мир!"); ЗначениеJSON = ПрочитатьЗначениеJSON(СтрокаJSON); // *** Тип Массив Массив = Новый Массив; Массив.Добавить(3.1415921654); Массив.Добавить(2.718281828); СтрокаJSON = ЗаписатьЗначениеJSON(Массив); ЗначениеJSON = ПрочитатьЗначениеJSON(СтрокаJSON); // *** Тип Структура Данные = Новый Структура; Данные.Вставить("Число", 3.141592654); Данные.Вставить("Строка", "Привет, Мир!"); Данные.Вставить("Булево", Истина); Массив = Новый Массив; Массив.Добавить(3.1415921654); Массив.Добавить(2.718281828); Данные.Вставить("Массив", Массив); СтрокаJSON = ЗаписатьЗначениеJSON(Данные); ЗначениеJSON = ПрочитатьЗначениеJSON(СтрокаJSON); // Исключение времени исполнения - попытка сериализации типа Дата СтрокаJSON = ЗаписатьЗначениеJSON('20220801');
Если для работы с JSON требуются какие-то дополнительные возможности (в том числе, связанные с форматированием вывода JSON-значений), то в это случае следует использовать другие техники работы.
Смотри также:
● HTTP-сервисы (см. здесь).
16.2.3. Объектная техника работы
16.2.3.1. Общая информация
При работе с объектной техникой, имеется возможность читать (и писать) данные в соответствие или структуру. Основное отличие между этими объектами состоит в том, что ключ элемента структуры подчиняется правилам формирования переменной на встроенном языке, а ключ элемента соответствия может быть любым. С учетом того, что JSON не накладывает ограничений на значение ключа, не все JSON-документы можно прочитать в структуру. Еще одним различием между структурой и соответствием является то, что к элементам структуры можно обращаться «через точку», а к элементам соответствия такой доступ не предоставляется. В связи с этим, может оказаться удобным получать данные в виде структуры, если ключи из JSON-документа соответствуют требованиям к ключам структур системы «1С:Предприятие».
Объектная техника предполагает достаточно простую работу с данными, однако платой за это является большой расход памяти, т. к. весь JSON-документ обрабатывается целиком в оперативной памяти.
16.2.3.2. Запись
Для того чтобы выполнить запись объекта в формате JSON, необходимо использовать (в простейшем случае) следующие объекты:
1. Собственно записываемый объект, например типа Структура.
2. Объект, обеспечивающий низкоуровневую запись данных в формате JSON ‑ ЗаписьJSON.
3. Объект настроек сериализации НастройкиСериализацииJSON.
Метод глобального контекста ЗаписатьJSON() оперирует вышеперечисленными объектами. Рассмотрим пример, в котором потребуется записать структуру, которая состоит из трех элементов разного типа (но типы являются примитивными):
Копировать в буфер обменаЗапись = Новый ЗаписьJSON; Запись.ОткрытьФайл("c:\temp\simpleWrite.json"); Данные = Новый Структура; Данные.Вставить("ДлинаЗаписи", 20); Данные.Вставить("КлючЗаписи", "abcdefgh"); Данные.Вставить("ДатаИзменения", ТекущаяДата()); ЗаписатьJSON(Запись, Данные, Новый НастройкиСериализацииJSON); Запись.Закрыть();
В результате работы данный пример сформирует следующий JSON-документ:
Копировать в буфер обмена{
"ДлинаЗаписи": 20,
"КлючЗаписи": "abcdefgh",
"ДатаИзменения": "2014-09-24T17:32:11"
}
Если в сериализуемых данных участвуют значения типа Дата, то может потребоваться управлять форматом сериализации. Для этого необходимо установить параметры объекта НастройкиСериализацииJSON. В следующем примере дата будет сериализоваться в формате JavaScript:
Копировать в буфер обменаНастройкиСериализации = Новый НастройкиСериализацииJSON; НастройкиСериализации.ФорматСериализацииДат = ФорматДатыJSON.JavaScript; НастройкиСериализации.ВариантЗаписиДаты = ВариантЗаписиДатыJSON.УниверсальнаяДата; Запись = Новый ЗаписьJSON; Запись.ОткрытьФайл("c:\temp\simpleWrite.json"); Данные = Новый Структура; Данные.Вставить("ДлинаЗаписи", 20); Данные.Вставить("КлючЗаписи", "abcdefgh"); Данные.Вставить("ДатаИзменения", ТекущаяДата()); ЗаписатьJSON(Запись, Данные, НастройкиСериализации); Запись.Закрыть();
В результате работы данный пример сформирует следующий JSON-документ:
Копировать в буфер обмена{
"ДлинаЗаписи": 20,
"КлючЗаписи": "abcdefgh",
"ДатаИзменения": "new Date(1411565832000)"
}
Если несколько расширить набор записываемых данных, например, добавить к ним значение типа УникальныйИдентификатор, то запись не будет выполнена. В результате исполнения кода:
Копировать в буфер обменаЗапись = Новый ЗаписьJSON; Запись.ОткрытьФайл("c:\temp\simpleWrite.json"); Данные = Новый Структура; Данные.Вставить("ДлинаЗаписи", 20); Данные.Вставить("КлючЗаписи", "abcdefgh"); Данные.Вставить("ДатаИзменения", ТекущаяДата()); Данные.Вставить("UID", Новый УникальныйИдентификатор); ЗаписатьJSON(Запись, Данные, Новый НастройкиСериализацииJSON); Запись.Закрыть();
Будет вызвано исключение:
Копировать в буфер обменаОшибка при вызове метода контекста (ЗаписатьJSON) ЗаписатьJSON(Запись, Данные, Новый НастройкиСериализацииJSON); по причине: Передано значение недопустимого типа
Причиной такого поведения является то, что тип УникальныйИдентификатор не входит в состав JSON-сериализуемых типов данных системы «1С:Предприятие». Однако система предоставляет возможность «обойти» это ограничение: необходимо передать в функцию ЗаписатьJSON() имя функции обратного вызова, которая будет заниматься JSON-сериализацией неподдерживаемых объектов. Эта функция будет называться функцией преобразования. При этом формат такой сериализации будет разрабатывать непосредственно сам прикладной разработчик. Надо понимать, что такая сериализация не будет универсальной, т. к. принимающая сторона, не обладающая знаниями о формате сериализации, не сможет прочитать переданные данные. Другими словами, формат сериализации необходимо разрабатывать совместно всеми сторонами обмена такого рода данными.
С учетом вышесказанного, более сложный вариант обмена теперь происходит следующим образом:
● Вызывается функция сериализации объекта в формат JSON (ЗаписатьJSON()).
● Система «1С:Предприятие» для каждого элемента структуры, тип значения которого не сериализуется в формат JSON, будет вызываться функция преобразования.
● Функция преобразования анализирует переданный объект и принимает решение ‑ отказаться от его записи или вернуть платформе значение, которое может быть сериализовано в JSON.
Доработанный код записи будет выглядеть следующим образом:
Копировать в буфер обмена&НаСервере Процедура ЗаписьНаСервере() Запись = Новый ЗаписьJSON; Запись.ОткрытьФайл("c:\temp\compositeWrite.json"); Данные = Новый Структура; Данные.Вставить("ДлинаЗаписи", 20); Данные.Вставить("КлючЗаписи", "abcdefgh"); Данные.Вставить("ДатаИзменения", ТекущаяДата()); Данные.Вставить("UID", Новый УникальныйИдентификатор); ЗаписатьJSON(Запись, Данные, Новый НастройкиСериализацииJSON, "ФункцияПреобразованияЗаписи", ЭтотОбъект); Запись.Закрыть(); КонецПроцедуры &НаСервере Функция ФункцияПреобразованияЗаписи(Свойство, Значение, ДополнительныеПараметры, Отказ) Экспорт Если ТипЗнч(Значение) = Тип("УникальныйИдентификатор") Тогда Возврат Строка(Значение); КонецЕсли; Отказ = Истина; КонецФункции
Следует обратить внимание, что функция преобразования должна быть объявлена с указанием ключевого слова Экспорт. Также следует помнить, что функция преобразования (в модуле управляемой формы) может быть описана только в «контекстной» части модуля, т. е. с использованием директивы компиляции &НаКлиенте или &НаСервере.
В результате работы приведенного примера будет сформирован следующий JSON-документ:
Копировать в буфер обмена{
"ДлинаЗаписи": 20,
"КлючЗаписи": "abcdefgh",
"ДатаИзменения": "2014-09-24T18:09:13",
"UID": "5a80e5dc-252b-416f-b487-d9ddcebc523e"
}
При создании функции преобразования следует помнить о следующих особенностях:
● Функция преобразования вызывается для значений всех типов, которые не поддерживают сериализацию в JSON (см. здесь).
● Если функция преобразования вернет значение, которое не может быть сериализовано в JSON ‑ будет вызвано исключение.
● Если переданное значение является структурой, то вызов функции преобразования для элементов этой структуры будет вызваться до того, как в поток будет записано имя свойства этого элемента. В результате функция преобразования может отказаться от записи значения и структура формируемого JSON-документа не будет нарушена.
● Имя свойства будет передано в функцию преобразования только для элементов структур и соответствий.
16.2.3.3. Чтение
16.2.3.3.1. Общая схема
Чтение данных в объектной технике выглядит аналогично записи. Рассмотрим пример чтения файлов, которые формировались во время рассмотрения объектной записи (предыдущий раздел).
Копировать в буфер обменаЧтение = Новый ЧтениеJSON; Чтение.ОткрытьФайл("c:\temp\simpleWrite.json"); Данные = ПрочитатьJSON(Чтение, Ложь, "ДатаИзменения", ФорматДатыJSON.JavaScript); Чтение.Закрыть();
Документ имеет следующее содержание (с точностью до значения даты):
Копировать в буфер обмена{
"ДлинаЗаписи": 20,
"КлючЗаписи": "abcdefgh",
"ДатаИзменения": "new Date(1411565832000)"
}
При чтении JSON-документа в переменную Данные будет сформирована структура вида:
Копировать в буфер обменаКлюч = ДлинаЗаписи, значение = 20 Ключ = КлючЗаписи, значение = abcdefgh Ключ = ДатаИзменения, значение = <значение даты и времени>
Такой вариант чтения хорошо подходит в том случае, если читаемые данные могут быть преобразованы в структуру или соответствие и все читаемые данные могут быть однозначно десериализованы без потери информации о типе. Если читаемые данные обладают сложной структурой или требуют выполнения дополнительных преобразований при чтении, то можно пойти двумя путями:
1. Получить соответствие (или структуру), в которое будет полностью загружен JSON-документ, и потом завершить преобразование с помощью обхода получившегося объекта.
2. Заниматься необходимым преобразованием непосредственно во время загрузки данных. Для этого необходимо передать в функцию ПрочитатьJSON() имя функции обратного вызова, которая будет заниматься десериализацией JSON-данных в нужные объекты системы «1С:Предприятие». Эта функция будет называться функцией восстановления.
В обоих случаях прикладной разработчик должен знать, какие данные и в каком виде находятся JSON-документе. Далее будут подробно рассмотрены оба варианта загрузки данных. В качестве макетной задачи будет рассматриваться получение погоды в г. Москва с помощью некоторого интернет-сервиса. В качестве ответа интернет-сервис возвращает JSON-документ следующего содержания:
Копировать в буфер обмена{
"id":524901,"name":"Moscow","dt":1411464940000,
"coord": { "lon":37.62,"lat":55.75 },
"sys": { "country":"RU","sunrise":1411442400000,"sunset":1411485660000 },
"weather": [
{ "main":"Clouds","description":"пасмурно" }
],
"main": { "temp":282.93,"pressure":1014,"humidity":93 },
"wind": { "speed":4,"deg":350 },
"clouds": { "all":90 }
}
Кратко рассмотрим описание формата:
● id ‑ идентификатор города;
● name ‑ имя города;
● dt ‑ дата и время получения погоды, в формате Unix, GMT;
● coord ‑ местоположение города:
● lon ‑ долгота;
● lan ‑ широта.
● sys ‑ дополнительная информация:
● country ‑ страна расположения города;
● sunrise ‑ время восхода Солнца в формате Unix, GMT;
● sunset ‑ время заката Солнца, в формате Unix, GMT.
● weather ‑ дополнительная информация о погоде:
● main ‑ общая характеристика погоды;
● description ‑ описание погоды.
● main ‑ собственно описание погоды:
● temp ‑ температура, в градусах Кельвина. Для получения градусов Цельсия необходимо вычесть 273.15;
● pressure ‑ давление в гектопаскалях. Для перевода в миллиметры ртутного столба, надо значение давления умножить на 0,75.
● humidity ‑ влажность в %.
● wind ‑ параметры ветра:
● speed ‑ скорость в милях в час. Для перевода в километры в час необходимо умножить на 1,61.
● deg ‑ направление ветра, в градусах.
● clouds ‑ информация об осадках:
● all ‑ вероятность возникновения осадков, в %.
В результате загрузки этих данных должна получиться структура, где все времена представлены стандартным типом Дата, температура ‑ в градусах Цельсия, скорость ‑ в километрах в час, а давление ‑ в миллиметрах ртутного столба.
Рассмотрим загрузку данной информации обоими способами. Данные записаны в файле c:\temp\weather.json.
16.2.3.3.2. Чтение с постобработкой
Собственно процесс чтения выглядит просто:
Копировать в буфер обменаЧтение = Новый ЧтениеJSON; Чтение.ОткрытьФайл("c:\temp\weather.json"); Данные = ПрочитатьJSON(Чтение, Ложь); Чтение.Закрыть();
В результате в переменной Данные будет следующая информация:

Рис. 506. Результат загрузки
Без учета необходимости конвертации все выглядит предсказуемо. Однако дата и время автоматически не преобразовались. Можно попробовать указать системе на то, что поле dt (например) является полем, где находится дата и время:
Копировать в буфер обменаЧтение = Новый ЧтениеJSON; Чтение.ОткрытьФайл("c:\temp\weather.json"); Данные = ПрочитатьJSON(Чтение, Ложь, "dt", ФорматДатыJSON.JavaScript); Чтение.Закрыть();
В результате получим сообщение об ошибке следующего вида:
Копировать в буфер обменаОшибка при вызове метода контекста (ПрочитатьJSON)
Данные = ПрочитатьJSON(Чтение, Ложь, "dt", ФорматДатыJSON.JavaScript);
по причине:
Представление даты имеет неверный формат
Ошибка произошла потому, что система не понимает формат даты и времени, выраженной простым числом. Программный интерфейс работы с JSON предлагает функцию, помогающую выполнить конвертацию полей типа Дата ‑ ПрочитатьДатуJSON(). Для использования этой функции необходимо привести десериализованную строку к формату, принятому, например, в JavaScript. В рассматриваемом примере получится следующий программный текст:
Копировать в буфер обменаДанные.dt = ПрочитатьДатуJSON("new Date("+Формат(Данные.dt, "ЧГ=")+")", ФорматДатыJSON.JavaScript);
В результате значение свойства Данные.dt станет равно значению 23.09.2014 13:35:40 (типа Дата). Остальная конвертация выполняется аналогичным образом.
16.2.3.3.3. Чтение с функцией восстановления
Вариант постобработкой выглядит не очень хорошо, если чтение JSON-документа предполагается выполнять в разных местах прикладного решения. В этом случае может возникнуть ситуация, когда код преобразования будет расположен в нескольких местах прикладного решения.
Хорошей заменой постобработки, в этом случае, является использование функции восстановления. Для применения функции восстановления необходимо описать саму функцию и несколько изменить само чтение документа:
Копировать в буфер обмена&НаСервере Функция ФункцияВосстановленияЧтения(Свойство, Значение, ДополнительныеПараметры) Экспорт Если Свойство = "dt" Или Свойство = "sunrise" Или Свойство = "sunset" Тогда Возврат ПрочитатьДатуJSON("new Date("+Формат(Значение, "ЧГ=")+")", ФорматДатыJSON.JavaScript); КонецЕсли; Если Свойство = "pressure" Тогда Возврат Значение*0.75; КонецЕсли; Если Свойство = "temp" Тогда Возврат Значение-273.15; КонецЕсли; Если Свойство = "speed" Тогда Возврат Значение*1.61; КонецЕсли; КонецФункции &НаСервере Процедура ЧтениеНаСервере() Чтение = Новый ЧтениеJSON; Чтение.ОткрытьФайл("c:\temp\weather.json"); Данные = ПрочитатьJSON(Чтение, Ложь, , , "ФункцияВосстановленияЧтения", ЭтотОбъект); Чтение.Закрыть(); КонецПроцедуры
В вышеуказанном примере присутствует особенность ‑ функция восстановления будет вызвана для всех свойств, которые будут обнаружены в JSON-документе. Это не всегда удобно и, кроме того, существенно снижает производительность чтения JSON-документа (за счет вызова функции восстановления). Например, при чтении метеорологических данных нам необходимо выполнять особое преобразование только для свойств, в которых содержится дата и время, а остальные свойства мы конвертировать не собираемся. Чтобы не анализировать в функции восстановления имя реквизита (как в вышеприведенном примере), можно поступить другим способом: явным образом передать в функцию чтения JSON-документа список реквизитов, которые содержат дату и время, а функцию восстановления написать исходя из того, что эта функция будет вызвана только для необходимых свойств. В результате получится следующий код:
Копировать в буфер обмена&НаСервере Процедура ЧтениеНаСервере() Чтение = Новый ЧтениеJSON; Чтение.ОткрытьФайл("c:\temp\weather.json"); Реквизиты = Новый Массив; Реквизиты.Добавить("dt"); Реквизиты.Добавить("sunrise"); Реквизиты.Добавить("sunset"); Данные = ПрочитатьJSON(Чтение, Ложь, , , "ФункцияВосстановленияЧтения", ЭтотОбъект, , Реквизиты); Чтение.Закрыть(); КонецПроцедуры &НаСервере Функция ФункцияВосстановленияЧтения(Свойство, Значение, ДополнительныеПараметры) Экспорт Возврат ПрочитатьДатуJSON("new Date("+Формат(Значение, "ЧГ=")+")", ФорматДатыJSON.JavaScript); КонецФункции
Следует обратить внимание, что функция восстановления должна быть объявлена с указанием ключевого слова Экспорт. Также следует помнить, что функция восстановления (в модуле управляемой формы) может быть описана только в «контекстной» части модуля, т. е. с использованием директивы компиляции &НаКлиенте или &НаСервере. При разработке функции восстановления необходимо принимать во внимание тот факт, что свойства документа считываются не в том порядке, как они представлены в файле.
Рассмотрим последовательность, в которой свойства JSON-документа попадают в функцию восстановления. Для этого разместим в таблице каждое свойство файла и то, в каком порядке будет прочитано свойство:
|
Данные JSON-документа |
Последовательность вызова функции восстановления |
|
{ "id":524901, |
1 |
|
"name":"Moscow", |
2 |
|
"dt":1411464940000, |
3 |
|
"coord": |
6 (структура) |
|
{ "lon":37.62, |
4 |
|
"lat":55.75 }, |
5 |
|
"sys": |
10 (структура) |
|
{ "country":"RU", |
7 |
|
"sunrise":1411442400000, |
8 |
|
"sunset":1411485660000 }, |
9 |
|
"weather": |
14 (массив) |
|
[ |
13 (структура) |
|
{ "main":"Clouds", |
11 |
|
"description":"пасмурно" } |
12 |
|
], |
|
|
"main": |
18 (структура) |
|
{ "temp":282.93, |
15 |
|
"pressure":1014, |
16 |
|
"humidity":93 }, |
17 |
|
"wind": |
21 (структура) |
|
{ "speed":4, |
19 |
|
"deg":350 }, |
20 |
|
"clouds": |
23 (структура) |
|
{ "all":90 } |
22 |
|
|
24 (структура) |
В общем случае, можно сформулировать следующее правило обхода: первым будет прочитано свойство, которому не подчинено ни одно другое свойство. Например, свойству id не подчинено никакое свойство, и оно считывается первым. Однако свойству coord подчинено свойства lon и lat, поэтому вначале будут считаны эти свойства, а лишь затем ‑ свойство coord, которое в качестве значения получит структуру (или соответствие) из подчиненных свойств документа.
16.2.4. Работа с XDTO-объектами
16.2.4.1. Общая информация
Работа с XDTO-объектами, в основном, ориентирована на обмен информации между системами, написанными на платформе «1С:Предприятие». Однако сам механизм не накладывает никаких ограничений на его использование для обмена с другими системами.
JSON-сериализация XDTO-объекта выполняется сразу в JSON-документ, без формирования в памяти полной структуры сериализуемых объектов. Также следует учитывать, что JSON-сериализация «эмулирует» XML-сериализацию, в силу чего получающийся JSON-документ внешне выглядит очень похоже на соответствующий XML-документ.
В JSON-документ могут быть помещены любые объекты системы «1С:Предприятие», для которых указано, что они могут быть сериализованы в XDTO (см. здесь). При попытке выполнить сериализацию значения неподдерживаемого типа будет вызвано исключение.
16.2.4.2. Запись
Для того чтобы выполнить запись XDTO-объекта в формате JSON, необходимо использовать (в простейшем случае) следующие объекты:
1. Собственно записываемый объект, поддерживающий преобразование в/из XDTO, например, элемент справочника.
2. Сериализатор XDTO-объектов ‑ СериализаторXDTO;
3. Объект, обеспечивающий низкоуровневую запись данных в формате JSON ‑ ЗаписьJSON.
4. Объект настроек сериализации НастройкиСериализацииJSON.
Собственно сериализация выполняется с помощью метода ЗаписатьJSON() объекта СериализаторXDTO. Рассмотрим пример сериализации данных типа СправочникОбъект. В качестве примера используется справочник Валюты, который содержит поля Курс (типа Число) и ДатаКурса (типа Дата):
Копировать в буфер обменаДанные = Справочники.Валюты.НайтиПоКоду("978").ПолучитьОбъект(); Запись = Новый ЗаписьJSON; Запись.ОткрытьФайл("c:\temp\simpleXDTOWrite.json"); СериализаторXDTO.ЗаписатьJSON(Запись, Данные); Запись.Закрыть();
В результате работы данный пример сформирует следующий JSON-документ:
Копировать в буфер обмена{
"#value": {
"Ref": "5b65bd8e-ea70-11e4-af93-e03f49b16069",
"DeletionMark": false,
"Code": "978",
"Description": "Евро",
"Курс": 54.659,
"ДатаКурса": "2015-04-25T00:00:00"
}
}
Сериализация значений типа Дата выполняется в формате ISO (определяется механизмом XDTO) и не управляется при записи данных. Также не поддерживается использование функции преобразования при операции сериализации, в отличие от потоковой (см. здесь) и объектной (см. здесь) техник.
Также следует помнить о следующей особенности: при записи объекта не формируется его тип, поэтому после JSON-сериализации XDTO-объекта отсутствует возможность выполнить десериализацию без указания типа считываемого объекта. Предыдущий пример сериализации элемента справочника Валюты будет невозможно десериализовать без явного указания типа значения. Чтобы упростить ситуацию, можно воспользоваться параметром НазначениеТипаXML метода ЗаписатьJSON() объекта СериализаторXDTO. Если в качестве значения этого параметра указать НазначениеТипаXML.Явное, то появится возможность выполнить десериализацию без явного указания типа, а сформированный файл будет выглядеть следующим образом:
Копировать в буфер обмена{
"#type": "jcfg:CatalogObject.Валюты",
"#value": {
"Ref": "5b65bd8e-ea70-11e4-af93-e03f49b16069",
"DeletionMark": false,
"Code": "978",
"Description": "Евро",
"Курс": 54.659,
"ДатаКурса": "2015-04-25T00:00:00"
}
}
В данном примере особого внимания заслуживает элемент #type, который и описывает тип текущего элемента. Описание префиксов пространств имен см. здесь.
Общие принципы JSON-сериализации объектов XDTO идентичны XML-сериализации:
● Структура данных соответствует структуре XML-документа.
● Имеются незначительные отличия, связанные с особенностями хранения типов и представлением массивов в JSON:
● Порядок и состав свойств определен в модели XDTO и не может быть изменен.
● Если тип записываемого значения однозначно определяется из типа, определенного в соответствующем свойстве XDTO-модели, то записывается только значение без идентификации типа.
● Если тип записываемого значения неоднозначно определяется из типа, определенного в соответствующем свойстве XDTO-модели (например, значение составного типа), то значение будет записано в виде сложного объекта JSON с отдельными свойствами для типа (свойство #type) и значения (свойство #value). В следующем примере записаны эквивалентные варианты представления значения типа Строка. Первая запись используется при неоднозначном определении типа, вторая ‑ при однозначном:
Копировать в буфер обменаПервая запись
{
"#type": "jxs:string",
"#value": "Строка с примером"
}
Вторая запись
"Строка с примером"
JSON-сериализация объектов XDTO имеет ряд особенностей (по сравнению с XML-сериализацией):
● Коллекции значений (например, значения типа Массив, Структура и т. д.) всегда записываются в виде массива JSON.
● Тип значения реквизита записывается в специальное свойство JSON с именем #type.
● Пространство имен типов записывается в специальное свойство JSON с именем #ns:
● Для встроенных в платформу пространств имен типы записываются без использования свойства #ns, но с указанием префикса:
Копировать в буфер обменаjxс:ChartOfCharacteristicTypesObject.ДемоПланВидовХарактеристик jxs:string jxs:decimal
Соответствие префиксов и пространств имен см. здесь.
● Если встретится схема, которая не имеет префикса, то имя схемы явно записывается с помощью свойства #ns:
Копировать в буфер обмена{
"#ns": "http://mycompany.com/data/enterprise/myscheme",
"#type": "СпециальныйОбъект",
"#value": …
}
Значения Неопределено и NULL сериализуются особым образом:
1. Неопределено:
Копировать в буфер обмена{
"#type": "jv8:Null",
"#value": ""
}
2. NULL:
Копировать в буфер обмена{
"ИмяРеквизита": null
}
С помощью JSON возможно выполнить сериализацию XDTO-объектов, которые не соответствуют какой-либо схеме. В этом случае используется явное описание типов реквизитов, одноименные свойства не будут объединяться в массив, а будут выводиться в JSON-документ поэлементно, в соответствии с XDTO-объектом.
Пример:
Копировать в буфер обмена{
"#type": "jxs:anyType",
"#value": {
"Filters": {
"Filter": {
"Name": "Recorder",
"Value": {
"#type": "jxs:anySimpleType",
"#value": "acc2d259-c8f3-11e2-b5da-5404a6a68c42"
}
}
},
"Record": {
"Recorder": {
"#type": "jxs:anySimpleType",
"#value": "acc2d259-c8f3-11e2-b5da-5404a6a68c42"
},
"Period": {
"#type": "jxs:anySimpleType",
"#value": "2012-09-08T00:00:00"
},
"Active": {
"#type": "jxs:anySimpleType",
"#value": "true"
},
},
"Record": {
"Recorder": {
"#type": "jxs:anySimpleType",
"#value": "acc2d259-c8f3-11e2-b5da-5404a6a68c42"
},
"Period": {
"#type": "jxs:anySimpleType",
"#value": "2012-09-08T00:00:00"
},
"Active": {
"#type": "jxs:anySimpleType",
"#value": "true"
},
},
}
}
16.2.4.3. Чтение
В общем случае, чтение XDTO-объекта из JSON-документа аналогично записи. Чтение выполняется с помощью механизма чтения XDTO-объектов из XML-файла, поэтому чтение выполняется со следующими ограничениями:
● Возможно чтение только тех объектов, для которых существует XDTO-сериализация.
● Свойства в JSON-документе должны следовать в том же порядке, как и в XDTO-объекте.
● В случае если читаемый объект не соответствует схеме ‑ будет вызвано исключение.
● Имеется возможность выполнять чтение произвольного JSON-документа в объект XDTO (ОбъектXDTO) с помощью фабрики XDTO (ФабрикаXDTO). Такое чтение возможно в том случае, если:
● фабрика XDTO, с помощью которой выполняется чтение, «знает» о типах, которые присутствуют в JSON-документе, из которого производится чтение.
● все элементы JSON указаны без явного указания типов и элементов, специфичных для JSON-документов, формируемых при сериализации объектов XDTO.
Выполнить чтение JSON-документа в том случае, если в нем используются типы, которые неизвестны фабрике XDTO, с помощью которой выполняется чтение документа ‑ невозможно.
Рассмотрим пример чтения некоторого JSON-документа, например, полученного при работе примера работы со справочником Валюты из предыдущего раздела (см. здесь).
Копировать в буфер обменаЧтение = Новый ЧтениеJSON; Чтение.ОткрытьФайл("c:\temp\simpleXDTOWrite.json"); Данные = СериализаторXDTO.ПрочитатьJSON(Чтение); Чтение.Закрыть();
В результате работы примера в переменную Данные будет помещен объект типа СправочникОбъект.Валюты для валюты с кодом 978. Однако данное поведение будет наблюдаться только в том случае, если при выполнении JSON-сериализация значение параметра НазначениеТипаXML было установлено в значение Явное. В противном случае при попытке чтения (как указано выше) будет вызвано исключение. При чтении объекта с неявным указанием типа объекта, читаемый тип можно передать в виде параметра метода ПрочитатьJSON(). В этом случае пример будет выглядеть следующим образом:
Копировать в буфер обменаЧтение = Новый ЧтениеJSON; Чтение.ОткрытьФайл("c:\temp\simpleXDTOWrite.json"); Данные = СериализаторXDTO.ПрочитатьJSON(Чтение, Тип("СправочникОбъект.Валюты")); Чтение.Закрыть();
При обмене данными между прикладными решениями с помощью объектов XDTO может возникать задача, когда требуется выполнить некоторую предобработку данных, которые поступают в пакете обмена. Например, заменить любое значение некоторого типа на фиксированное значение того же типа. Для упрощения такой предобработки предназначена функция восстановления. Имя функции восстановления передается в функцию чтения из JSON-документа для сериализатора или фабрики XDTO. При чтении объекта функция восстановления будет вызываться при десериализации значений следующих типов:
● Булево;
● Число;
● Строка;
● Дата;
● УникальныйИдентификатор;
● ДвоичныеДанные;
● ХранилищеЗначения;
● Значения перечислений;
● Значения системных перечислений;
● Ссылки на объекты базы данных.
Для значений Неопределено функция восстановления не вызывается.
При разработке функции восстановления необходимо помнить, что в этой функции можно заменить значение, которое считано из JSON-документа, но нельзя изменить тип считываемого объекта. Функция восстановления также не будет вызываться для служебных свойств, которые создаются и используются системой «1С:Предприятие», например, #type, #ns и т. д.
16.2.5. Потоковая техника работы
16.2.5.1. Общая информация
Потоковая техника работы с документом ориентирована на то, что документ целиком не загружается в память и обрабатывается последовательно, от элемента к элементу. Например, если надо прочитать только какой-то объект из JSON-документа, то потоковая техника может дать существенный выигрыш, особенно в том случае, если требуемый элемент находится в начале обрабатываемого документа.
16.2.5.2. Запись
Для того чтобы выполнить потоковую запись JSON-документа, необходимы записываемые данные и объект ЗаписьJSON. При этом следует понимать, что формирование корректной структуры JSON-документа полностью лежит на прикладном разработчике, который формирует документ. Для упрощения такого контроля объект ЗаписьJSON имеет свойство ПроверятьСтруктуру.
Рассмотрим простой пример записи документа:
Копировать в буфер обменаПараметрыJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, " ", Истина); Запись = Новый ЗаписьJSON; Запись.ПроверятьСтруктуру = Истина; Запись.ОткрытьФайл("c:\temp\streamWrite.json", , , ПараметрыJSON); Запись.ЗаписатьНачалоОбъекта(); Запись.ЗаписатьИмяСвойства("ДлинаЗаписи"); Запись.ЗаписатьЗначение(20); Запись.ЗаписатьИмяСвойства("Товар"); Запись.ЗаписатьНачалоОбъекта(); Запись.ЗаписатьИмяСвойства("Код"); Запись.ЗаписатьЗначение("0020"); Запись.ЗаписатьИмяСвойства("Наименование"); Запись.ЗаписатьЗначение("Товар"); Запись.ЗаписатьКонецОбъекта(); Запись.ЗаписатьИмяСвойства("МассивЧисел"); Запись.ЗаписатьНачалоМассива(); Запись.ЗаписатьЗначение(3.141592654, Истина); Запись.ЗаписатьЗначение(2.718281828, Ложь); Запись.ЗаписатьКонецМассива(); Запись.ЗаписатьКонецОбъекта(); Запись.Закрыть();
В результате исполнения этого программного кода будет сформирован следующий документ:
Копировать в буфер обмена{
"ДлинаЗаписи": 20,
"Товар": {
"Код": "0020",
"Наименование": "Товар"
},
"МассивЧисел": [
3.141592654E0,
2.718281828
]
}
Такой формат документа удобен для визуального просмотра, но занимает больше места. Можно изменить значение первого параметра конструктора ПараметрыЗаписиJSON на значение ПереносСтрокJSON.Нет и результирующий документ примет такой вид (разница составит примерно 20%):
Копировать в буфер обмена{"ДлинаЗаписи":20,"Товар":{"Код":"0020","Наименование":"Товар"},"МассивЧисел":[3.141592654E0,2.718281828]}
Если необходимо получить JSON-документ без формирования файла, то можно использовать метод УстановитьСтроку() объекта ЗаписьJSON. После вызова этого метода, для получения строки со сформированным JSON-документом, достаточно просто завершить запись документа методом Закрыть() объекта ЗаписьJSON:
Копировать в буфер обменаПараметрыJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет, " ", Истина); Запись = Новый ЗаписьJSON; Запись.УстановитьСтроку(ПараметрыJSON); … Документ = Запись.Закрыть();
Теперь данный документ (из переменной Документ) можно передавать, например, в тело HTTP-запроса.
16.2.5.3. Чтение
Потоковое чтение JSON-документа выполняется аналогично его записи: прикладной разработчик в цикле читает следующий элемент, определяет, что считано и обрабатывает считываемые данные.
Примитивный случай потокового чтения документа может выглядеть следующим образом:
Копировать в буфер обменаЧтение = Новый ЧтениеJSON; Чтение.ОткрытьФайл("c:\temp\streamWrite.json"); Пока Чтение.Прочитать() Цикл Сообщить("Тип текущего элемента " + Чтение.ТипТекущегоЗначения); Если Чтение.ТипТекущегоЗначения = ТипЗначенияJSON.ИмяСвойства Тогда Сообщить("Имя = " + Чтение.ТекущееЗначение); КонецЕсли; Если Чтение.ТипТекущегоЗначения = ТипЗначенияJSON.Булево Или Чтение.ТипТекущегоЗначения = ТипЗначенияJSON.Строка Или Чтение.ТипТекущегоЗначения = ТипЗначенияJSON.Число Или Чтение.ТипТекущегоЗначения = ТипЗначенияJSON.Комментарий Тогда Сообщить("Значение = " + Чтение.ТекущееЗначение); КонецЕсли; КонецЦикла; Чтение.Закрыть();
Тогда при чтении документа, сформированного при рассмотрении потоковой записи JSON-документа (см. здесь), будет сформирован следующий результат:
Копировать в буфер обменаТип текущего элемента Начало объекта Тип текущего элемента Имя свойства Имя = ДлинаЗаписи Тип текущего элемента Число Значение = 20 Тип текущего элемента Имя свойства Имя = Товар Тип текущего элемента Начало объекта Тип текущего элемента Имя свойства Имя = Код Тип текущего элемента Строка Значение = 0020 Тип текущего элемента Имя свойства Имя = Наименование Тип текущего элемента Строка Значение = Товар Тип текущего элемента Конец объекта Тип текущего элемента Имя свойства Имя = МассивЧисел Тип текущего элемента Начало массива Тип текущего элемента Число Значение = 3,141592654 Тип текущего элемента Число Значение = 2,718281828 Тип текущего элемента Конец массива Тип текущего элемента Конец объекта
16.2.6. Совмещение техник
Для упрощения работы с JSON, можно совмещать различные техники при формировании одного документа. Например, необходимо сформировать документ, который содержит в себе некоторый набор структур и массив. В этом случае можно все оформление документа выполнять с помощью потоковой техники, а уже готовые структуры и массив записывать с помощью объектной техники. Важно только обеспечить корректную структуру документа перед началом объектной записи.
Приведем пример совмещения техник на следующем примере:
● В ответ на запрос внешней системы, этой системе необходимо возвращать список заказов.
● Список должен содержать дату формирования и набор заказов (соответствующий некоторому критерию).
● Каждый заказ описывается следующими параметрами:
1. Номер заказа;
2. Дата формирования заказа;
3. Контрагент по заказу;
4. Уникальный идентификатор заказа, по которому впоследствии можно получить всю информацию по заказу.
Фрагмент кода на встроенном языке, который формирует JSON-документ, будет иметь следующий вид:
Копировать в буфер обменаНастройкиСериализации = Новый НастройкиСериализацииJSON; НастройкиСериализации.ВариантЗаписиДаты = ВариантЗаписиДатыJSON.УниверсальнаяДата; НастройкиСериализации.ФорматСериализацииДат = ФорматДатыJSON.ISO; НастройкиСериализации.СериализовыватьМассивыКакОбъекты = Истина; ПараметрыJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, " ", Истина); Запись = Новый ЗаписьJSON; Запись.ПроверятьСтруктуру = Истина; Запись.ОткрытьФайл("c:\temp\combinedWrite.json", , , ПараметрыJSON); Запись.ЗаписатьНачалоОбъекта(); Запись.ЗаписатьИмяСвойства("ДатаФормирования"); Запись.ЗаписатьЗначение(ЗаписатьДатуJSON(КогдаСформировано, ФорматДатыJSON.ISO, ВариантЗаписиДатыJSON.УниверсальнаяДата)); Запись.ЗаписатьИмяСвойства("Заказы"); Заказы = Новый Массив; Для каждого Заказ Из СписокЗаказов Цикл ОписаниеЗаказа = Новый Структура("Ссылка, Номер, Дата, Контрагент"); ОписаниеЗаказа.Ссылка = Строка(Заказ.Ссылка); ОписаниеЗаказа.Номер = Заказ.Номер; ОписаниеЗаказа.Дата = Заказ.Дата; ОписаниеЗаказа.Контрагент = Строка(Заказ.Контрагент); Заказы.Добавить(ОписаниеЗаказа); КонецЦикла; ЗаписатьJSON(Запись, Заказы, НастройкиСериализации); Запись.ЗаписатьКонецОбъекта(); Запись.Закрыть();
При исполнении данного кода предполагается, что:
● Переменная КогдаСформировано содержит значение типа Дата. Содержит дату и время формирования JSON-документа.
● Переменная СписокЗаказов является массивом ссылок на документы заказов.
Исполнение данного код приведет к формированию следующего JSON-документа:
Копировать в буфер обмена{
"ДатаФормирования": "2014-10-06T12:57:35Z",
"Заказы": {
"0": {
"Ссылка": "f4d1495a-02b5-4d56-92c6-840c11dfb592",
"Номер": 234,
"Дата": "2014-09-30T20:00:00Z",
"Контрагент": "Иванов И.И."
},
"1": {
"Ссылка": "ee821799-2d57-475e-a330-f414e53b8bda",
"Номер": 436,
"Дата": "2014-09-24T20:00:00Z",
"Контрагент": "Петров А.П."
},
"2": {
"Ссылка": "e058a5a8-3c0d-453b-8b1c-963a35fe2b7a",
"Номер": 118,
"Дата": "2014-08-31T20:00:00Z",
"Контрагент": "Иванов И.И."
}
}
}
Изменяя значения параметров объектов НастройкиСериализации и ПараметрыJSON, а также манипулируя параметрами метода ЗаписатьДатуJSON(), можно изменять результирующий JSON-документ для максимального соответствия «ожиданиям» принимающей системы.
16.3. Работа с двоичными данными
16.3.1. Общая информация
При реализации прикладных решений возможны ситуации, когда необходимо анализировать различные двоичные данные. Например, требуется по сигнатуре определить тип файла или выполнить какие-либо манипуляции с картинкой. Для работы с двоичными данными «1С:Предприятие» предоставляет специальные программные интерфейсы. Далее будут рассмотрены возможности по работе с двоичными данными.
Собственно хранение двоичных данных во время работы системы выполняется с помощью одноименного объекта ‑ ДвоичныеДанные. С помощью этого объекта можно прочитать данные любого файла, не обращая внимания на формат этого файла. Но модификация данных с помощью данного объекта не поддерживается. Единственным исключением можно считать возможность разбить двоичные данные на несколько частей указанного размера (метод РазделитьДвоичныеДанные()) и выполнить обратную операцию (метод СоединитьДвоичныеДанные()).
Вся дальнейшая работа с двоичными данными базируется на понятии потока. Поток ‑ это логическое обобщение произвольного (в общем случае) источника данных (объект Поток). Система не предоставляет возможности создать самостоятельный объект Поток, который не связан с каким-либо источником. Но существуют производные объекты, которые можно создать ‑ это поток, связанный с файлом на диске (объект ФайловыйПоток) или поток, созданный в памяти (объект ПотокВПамяти). Поток предоставляет возможность как чтения данных, так и их записи. Для определения возможности выполнения тех или иных операций, у потока (и производных объектов) существуют специальные методы, позволяющие определить, какие операции доступны с данным потоком (методы ДоступнаЗапись(), ДоступноЧтение(), ДоступноИзменениеПозиции()).
Если необходимо работать с потоком на более высоком уровне, в частности, выполнять чтение/запись таких данных, как число (разной разрядности) или строка, то для этого предназначены объекты ЧтениеДанных/ЗаписьДанных. С помощью этих объектов имеется возможность более структурировано подходить к двоичным данным, которые расположены в потоке. Так, например, зная формат какого-либо файла, можно достаточно комфортно выполнять чтение такого файла, получая из заголовков нужные данные (которые, как правило, представлены типами число и строка), пропуская не нужные блоки данных и загружая для обработки нужные.
Общую схему работы с двоичными данными можно представить следующим образом:
1. Выполняется получение потока.
2. Создается объект ЧтениеДанных или ЗаписьДанных.
3. С помощью объекта, созданного в п.2, выполняются требуемые действия.
4. Закрывается объект, созданный в п.2.
5. Если никаких операций больше выполнять не требуется ‑ закрывается поток, полученный в п.1.
6. Если требуется продолжить работу с потоком, то можно выполнить установку новой позиции в потоке (если эта операция поддерживается) и продолжить работу, начиная с п.2.
Стоит отметить, что имеется возможность объединить пп.1 и 2. Другими словами, система предоставляет возможность создания объектов ЧтениеДанных/ЗаписьДанных сразу из, например, объекта ДвоичныеДанные.
Для выполнения различных операций с двоичными данными система предоставляет возможность получить некоторую часть потока в качестве обособленного фрагмента с произвольным (побайтовым) доступом (объект БуферДвоичныхДанных). Размер буфера задается при создании и не может быть изменен в дальнейшем. При работе с буфером двоичных данных имеется возможность работать с числами разной разрядности как с единым целым. При этом имеется возможность указания порядка следования байтов в словах: «младший-старший» (little endian) или «старший-младший» (big endian). Также имеется возможность разбиения одного буфера на несколько и объединения нескольких буферов двоичных данных в один результирующий буфер.
Важно отметить, что работа с буфером двоичных данных позволяет существенно упростить реализацию в том случае, если работа с двоичными данными реализуется на стороне клиентского приложения в асинхронном режиме. В этом случае чтение данных в буфер будет выполняться асинхронной операцией, а работа с данными буфера является синхронной.
Работа с двоичными данными доступна на стороне клиентского (включая веб-клиент) приложения, на стороне сервера системы «1С:Предприятие», а также в синхронной и асинхронной схемах работы. Дальнейшие примеры будут использовать синхронную схему работы.
16.3.2. Чтение двоичных данных
В качестве примера чтения двоичных данных будет рассмотрена задача определения корректного формата файла, который выбрали в системе для дальнейшего использования. В роли проверяемого файла будет использоваться .wav-файл с аудио-данными. Для хранения .wav-файлов используется Resource Interchange File Format (RIFF), описание которого приведено по ссылке: https://msdn.microsoft.com/en-us/library/windows/desktop/ee415713.aspx (на английском языке). Для примера чтения будут использоваться следующие данные о формате:
1. Первые 4 байта файла содержат идентификатор формата: RIFF.
2. Следующие 4 байта содержат размер собственно аудио данных с порядком следования байт little-endian.
3. Следующие 4 байта содержат текстовый тип используемых данных: WAVE.
Для выполнения этих действий потребуется следующий код на встроенном языке:
Копировать в буфер обменаЧтение = Новый ЧтениеДанных(ИмяФайла, , ПорядокБайтов.LittleEndian); ФорматФайла = Чтение.ПрочитатьСимволы(4); РазмерДанных = Чтение.ПрочитатьЦелое32(); ТипФайла = Чтение.ПрочитатьСимволы(4); Если ФорматФайла <> "RIFF" Тогда Сообщить("Это не файл формата RIFF"); Возврат; КонецЕсли; Если ТипФайла = "WAVE" Тогда Сообщить("Это WAV файл с данными, размером " + РазмерДанных + " байт"); Иначе Сообщить("Это не WAV файл"); Возврат; КонецЕсли;
Рассмотрим пример более подробно.
Вначале открывается файл, имя которого содержится в переменной ИмяФайла, файл открывается для чтения (РежимОткрытияФайла.Открыть), из файла будут только читать (ДоступКФайлу.Чтение) и для чтения будет использоваться буфер размером 16 байт.
Затем формируется поток, предназначенный для чтения данных, который будет иметь порядок следования байтов «младший-старший» для данных типа Число. Затем из получившегося потока считывается 4 символа ASCII, 32-разрядное целое и еще 4 символа ASCII. Получившиеся данные анализируются, и по результатам анализа принимается решение о том, является выбранный файл .wav-файлом или нет.
16.3.3. Запись двоичных данных
Запись двоичных данных в файл, в простейшем случае, выполняется следующим образом:
Копировать в буфер обменаЗапись = Новый ЗаписьДанных(ИмяФайла); Для Индекс=0 По 255 Цикл Запись.ЗаписатьБайт(Индекс); КонецЦикла; Запись.Закрыть();
Данный пример выполняет запись в файл последовательности байтов от 0 до 255 (0xFF в 16-ричной системе). Это самый простой вариант записи.
Также можно использовать способ, аналогичный способу чтения, рассмотренному в предыдущем примере, когда получается файловый поток и в этот файловый поток выполняется запись данных.
16.3.4. Работа с буфером двоичных данных
Как уже было сказано выше, буфер двоичных данных предоставляет удобный способ по манипуляции фрагментами двоичных данных. Поддерживается не только чтение данных, но и запись.
В качестве примера будет рассмотрен разбор заголовка RIFF-файла из примера чтения данных (см. здесь). Для построения примера будут использованы ровно та же информация о формате файла. Таким образом, необходимо прочитать из исходного файла буфер размером с заголовок файла. Заголовок состоит из трех 4-байтовых полей. Таким образом, необходимо прочитать 12 байт.
Копировать в буфер обменаБуфер = Новый БуферДвоичныхДанных(12); Файл = ФайловыеПотоки.Открыть(КаталогВременныхФайлов() + "Windows Logon.wav", РежимОткрытияФайла.Открыть, ДоступКФайлу.Чтение); Файл.Прочитать(Буфер, 0, 12); Размер = Буфер.ПрочитатьЦелое32(4); ПотокСтроки = Новый ПотокВПамяти(Буфер); ПотокСтроки.Перейти(0, ПозицияВПотоке.Начало); ЧтениеСтроки = Новый ЧтениеДанных(ПотокСтроки); ФорматФайла = ЧтениеСтроки.ПрочитатьСимволы(4, "windows 1251"); ЧтениеСтроки.Закрыть(); ПотокСтроки.Перейти(8, ПозицияВПотоке.Начало); ЧтениеСтроки = Новый ЧтениеДанных(ПотокСтроки); ТипФайла = ЧтениеСтроки.ПрочитатьСимволы(4, "windows 1251"); ЧтениеСтроки.Закрыть();
Процесс получения данных в буфер двоичных данных не представляет из себя ничего особенного. Дальнейшие операции требуют некоторых комментариев. Чтение чисел любой поддерживаемой разрядности возможно из любой позиции буфера. В данном примере Буфер.ПрочитатьЦелое32(4); означает чтение 32-разрядного целого числа, начиная с 4 байта буфера. Таким образом, если требуется прочитать несколько чисел, расположенных в разных местах буфера, это можно сделать без прямого позиционирования в этом буфере.
Чтение строки, однако, не поддерживается буфером двоичных данных. Поэтому следует воспользоваться объектом, который это позволяет сделать: ЧтениеДанных. Объект ЧтениеДанных не может быть создан на основании буфера двоичных данных. Но на основании буфера двоичных данных можно создать поток, который является универсальным посредником между физическим местом хранения информации (файл, буфер двоичных данных) и высокоуровневым объектом, позволяющим работать с этими данными.
Когда создается объект ЧтениеДанных на основании какого-либо потока, он начинает читать данные с той позиции, которая в данный момент установлена в потоке. Поэтому в примере вначале происходит установка позиции в потоке, а потом создается объект ЧтениеДанных и выполняется чтение нужного количества символов. Подробное описание разницы между числом байтов и символов при чтении строк см. здесь.
16.3.5. Значения в двоичной и шестнадцатеричной системах и логические операции
При работе с различными бинарными форматами достаточно сильно распространены так называемые «магические числа» ‑ константы или битовые маски. Такие магические числа можно перевести в десятичные числа, но такое преобразование повышает вероятность ошибок и менее наглядно. Для того чтобы упростить работы с такими магическими числами, существуют функции глобального контекста ЧислоИзШестнадцатеричнойСтроки() и ЧислоИзДвоичнойСтроки().
Пример:
Копировать в буфер обменаМаскаСтаршегоБита = ЧислоИзДвоичнойСтроки("0b10000000"); НаборПараметров = ЧислоИзШестнадцатеричнойСтроки("0x79E");
Также система предоставляет возможность выполнять логические операции с буферами двоичных данных. Поддерживаются следующие операции:
● логическое «И» (метод ЗаписатьПобитовоеИ());
● логическое «Или» (метод ЗаписатьПобитовоеИли());
● логическое «исключающее Или» (метод ЗаписатьПобитовоеИсключающееИли());
● логическое «И Не» (обратная операция к логическому «И», метод ЗаписатьПобитовоеИНе());
● инверсия (метод Инвертировать()).
С помощью данных методов можно выполнять различные логические операции над данными, включая проверки установки конкретных битов. Данные методы оперируют с буферами двоичных данных. Таким образом, разрядность операции ограничена размером буфера. Пример использования этих методов для выполнения приведены в следующем разделе (см. здесь).
Однако значительно чаще возникает необходимость выполнять логические операции с числами ограниченной разрядности. Платформа предлагает набор специальных методов, который упрощает манипулирование отдельными битами в числах ограниченной разрядности:
● функции работы с битами и масками: ПроверитьБит(), ПроверитьПоБитовойМаске(), УстановитьБит().
● побитовые логические операции: ПобитовоеИ(), ПобитовоеИли(), ПобитовоеНе(), ПобитовоеИНе(), ПобитовоеИсключительноеИли().
● операции сдвига: ПобитовыйСдвигВправо(), ПобитовыйСдвигВлево().
При работе с вышеперечисленными функциями следует помнить о следующем:
● Все операнды рассматриваются как 32-разрядные беззнаковые целые числа. Попытка передачи в качестве параметра другого значения трактуется как неверное значение параметра с формированием исключения.
● В качестве номера бита выступает целое число от 0 (самый младший бит) до 31 (самый старший бит).
● При выполнении операций сдвига освободившиеся разряды всегда заполняются значением 0.
16.3.6. Особенности использования
Работа со строками
При использовании двоичных данных следует учитывать особенности работы с данными типа Строка. Особенность заключается в том, что длина строки, которую возвращает функция глобального контекста СтрДлина(), измеряется в символах. В символах же следует указывать размеры читаемых/записываемых данных в методах записи/чтения строк в объектах работы с двоичными данными (ПрочитатьСимволы(), ПрочитатьСтроку(), ЗаписатьСимволы(), ЗаписатьСтроку()). При этом не существует однозначного варианта преобразования длины строки в символах в аналогичный параметр в байтах. В зависимости от содержимого строки и кодировки, это соотношение будет разным. Например, в кодировке UTF-8 один символ может занимать от одного до шести байт. Поэтому при работе с какими-либо структурами данных, в состав которых входят строки переменной длины, следует четко понимать, в каких единицах выражены длины строк.
В том случае, когда необходимо выполнить конвертацию строки в байтовый набор и обратно, следует воспользоваться специальными методами ПолучитьДвоичныеДанныеИзСтроки()/ПолучитьБуферДвоичныхДанныхИзСтроки() и обратных к ним ПолучитьСтрокуИзДвоичныхДанных()/ПолучитьСтрокуИзБуфераДвоичныхДанных().
Данные методы позволяют указать кодировку строки и необходимость использования BOM (Byte Order Mark, маркер последовательности байт для кодировки Unicode). При конвертации строки в байтовый набор, размер получившейся строки можно получить через стандартный интерфейс определения размера получившегося объекта (ДвоичныеДанные.Размер() или БуферДвоичныхДанных.Размер).
Таким образом, получить двоичные данные из строки можно следующим образом:
Копировать в буфер обменаДанные = ПолучитьДвоичныеДанныеИзСтроки("Пример строки для конвертации", "UTF 8", Ложь); РазмерСтрокиВБайтах = Данные.Размер();
Обратная операция выполняется следующим образом:
1. Из потока чтения выполняется получение буфера двоичных данных требуемого размера.
2. Получившийся буфер двоичных данных преобразуется в строку.
На встроенном языке данная операция будет выглядеть следующим образом:
Копировать в буфер обмена// ПотокЧтения - существующий поток чтения Буфер = ПотокЧтения.ПрочитатьВБуферДвоичныхДанных(ДлинаСтрокиВБайтах); СчитаннаяСтрока = ПолучитьСтрокуИзБуфераДвоичныхДанных(Буфер);
Совместное использование разных объектов
Не рекомендуется одновременное использование объектов ЧтениеДанных/ЗаписьДанных и потоковых объектов. Если между двумя последовательными операциями чтения из ЧтениеДанных или двумя последовательными операциями записи в ЗаписьДанных происходит изменение позиции в потоке, с которым работают объекты ЧтениеДанных/ЗаписьДанных ‑ генерируется исключение. Так, следующий пример демонстрирует корректное изменение позиции в потоке при записи данных в поток:
Копировать в буфер обменаПоток = Новый ПотокВПамяти(); ЗаписьДанных = Новый ЗаписьДанных(Поток); ЗаписьДанных.ЗаписатьСтроку("Привет, мир!"); ЗаписьДанных.Закрыть(); Поток.Перейти(0, ПозицияВПотоке.Начало); ЗаписьДанных = Новый ЗаписьДанных(Поток); ЗаписьДанных.ЗаписатьСтроку("Пока!"); ЗаписьДанных.Закрыть();
Следующий пример приведет к возникновению исключения:
Копировать в буфер обменаПоток = Новый ПотокВПамяти(); ЗаписьДанных = Новый ЗаписьДанных(Поток); ЗаписьДанных.ЗаписатьСтроку("Привет, мир!"); Поток.Перейти(0, ПозицияВПотоке.Начало); // В следующей строке будет сгенернировано исключение ЗаписьДанных.ЗаписатьСтроку("Пока!");
В то же время, возможны ситуации, когда поведение системы будет некорректно, но никаких ошибок формироваться не будет:
Копировать в буфер обменаПоток = ПолучитьПоток(); ЧтениеДанных = Новый ЧтениеДанных(Поток); ТестоваяСтрока = ЧтениеДанных.Прочитать(); ИсходнаяПозиция = Поток.ТекущаяПозиция(); ЗаписьДанных = Новый ЗаписьДанных(Поток); ЗаписьДанных.ЗаписатьСтроку("Неожиданная строка"); ЗаписьДанных.Закрыть(); Поток.Перейти(ИсходнаяПозиция, ПозицияВПотоке.Начало); // В общем случае невозможно определить, какое значение будет помещено в переменную ТестоваяСтрока2 ТестоваяСтрока2 = ЧтениеДанных.ПрочитатьСтроку();
Описанное в данном разделе поведение вызвано тем, что объекты ЧтениеДанных/ЗаписьДанных используют собственные буфера при работе с потоком. В результате фактическая позиция потока отличается от логической позиции, которая сформирована как результат совершенных операций.
Также не поддерживается одновременное использование объектов ЧтениеДанных и ЗаписьДанных, которые используют для своей работы один поток.
Побитовые операции без использования буфера
Достаточно распространенной операцией является проверка и установка какого-либо разряда в наборе данных, который, например, хранит набор булевых флагов. Если набор данных представлен беззнаковым целым не более 32 разрядов «длиной», то для проверки какого-либо разряда можно использовать специальные функции. Далее будет приведен пример их (функций) использования. Если разрядность операндов превышает 32 разряда, то рекомендуется использовать следующий пример данного раздела.
Для проверки состояния какого-либо бита числа можно использовать две функции: ПроверитьБит() и ПроверитьПоБитовойМаске(). Разница заключается в том, что с помощью второй функции можно проверить одновременно несколько флагов в одном значении.
Собственно использование данной функции (ЛогическоеИ()) можно проиллюстрировать следующим примером:
Копировать в буфер обменаЧТЕНИЕ = ЧислоИзДвоичнойСтроки("0b00000001"); // 0 й бит СОЗДАНИЕ = ЧислоИзДвоичнойСтроки("0b00000010"); // 1 й бит ИЗМЕНЕНИЕ = ЧислоИзДвоичнойСтроки("0b00000100"); // 2 й бит УДАЛЕНИЕ = ЧислоИзДвоичнойСтроки("0b00001000"); // 3 й бит ФлагиДоступа = ЧислоИзДвоичнойСтроки("0b00001110"); // проверка конкретного бита ЧтениеВозможно = ПроверитьБит(ФлагиДоступа, 0); // проверка по маске (несколько флагов) ЗаписьВозможна = ПроверитьПоБитовойМаске(ФлагиДоступа, СОЗДАНИЕ+ИЗМЕНЕНИЕ);
В данном примере рассматривается некий байт, который содержит флаги, определяющие возможности работы с предполагаемым файлом. Каждый флаг определяется своей маской (переменные ЧТЕНИЕ, СОЗДАНИЕ, ИЗМЕНЕНИЕ, УДАЛЕНИЕ). В вышеприведенном примере значение ФлагиДоступа описывает следующий набор флагов:
● Чтение недоступно. 0-й бит сброшен.
● Создание возможно. 1-й бит установлен.
● Изменение возможно. 2-й бит установлен.
● Удаление возможно. 3-й бит установлен.
Проверка конкретного бита выполняется во время проверки возможности чтения по представленной маске. Если требуется проверить какой-либо другой разряд ФлагиДоступа, то при вызове функции ПроверитьБит() необходимо изменить значение второго параметра. Однако если требуется проверить одновременно несколько разрядов, то вместо функции ПроверитьБит() следует использовать метод ПроверитьПоБитовойМаске(). Вторым параметром этой функции передается маска проверки, которая содержит значения 1 в проверяемых разрядах. Если все требуемые разряды в маске также содержат 1 ‑ результатом работы функции будет значение Истина. В примере выполняется проверка одновременно двух разрядов: СОЗДАНИЕ и ИЗМЕНЕНИЕ. Сложение этих двух значений дает требуемую маску, проверка которой выполняется с помощью метода ПроверитьПоБитовойМаске().
Упрощение побитовых операций
Достаточно распространенной операцией является проверка и установка какого-либо разряда в наборе данных, который, например, хранит набор булевых флагов. Если значения не превышают 32 разрядов в «длину», то такие проверки описаны в предыдущем примере данного раздела. Если используются более «длинные» числа (разрядностью более 32 разрядов), то требуемые функции можно реализовать с помощью встроенного языка. Следует понимать, что так-как система типов «1С:Предприятия» не предполагает наличие специализированных без знаковых типов ограниченной разрядности, все дальнейшие методы опираются на предположение о том, что передаются целые, без знаковые числа. Несмотря на выше сказанное (про разрядность более 32 разрядов) примеры оперируют числами разрядностью 8 бит. Это сделано для упрощения примера. Данный пример также не учитывает порядок следования байт в буфере. Самое младшее значение всегда располагается в самом младшем байте формируемого буфера двоичных данных. Это предположение распространяется на оба следующих вспомогательных метода.
Для реализации такой операции потребуются две вспомогательные функции, первая из которых будет преобразовывать переданное число в буфер двоичных данных необходимого размера, а вторая будет выполнять обратную операцию ‑ из буфера двоичных данных формировать значение типа Число.
Копировать в буфер обменаФункция РазбитьЧислоНаБайты(Знач Число, ЧислоБайт = 0, Заполнитель = 0) // разбиваем переданное число на байты Число = Цел(Число); ПоБайтныйМассив = Новый Массив; Пока Число > 0 Цикл ПоБайтныйМассив.Добавить(Число % 256); Число = Цел(Число / 256); КонецЦикла; // если требуется какая то конкретная разрядность числа - здесь сделаем это Если ЧислоБайт <> 0 Тогда Если ЧислоБайт < ПоБайтныйМассив.Количество() Тогда // усечем результат до заданного размера Пока ПоБайтныйМассив.Количество() > ЧислоБайт Цикл ПоБайтныйМассив.Удалить(ЧислоБайт); КонецЦикла; Иначе // добавим старшие байты из заполнителя Разница = ЧислоБайт - ПоБайтныйМассив.Количество(); Пока Разница <> 0 Цикл ПоБайтныйМассив.Добавить(Заполнитель); Разница = Разница - 1; КонецЦикла; КонецЕсли; КонецЕсли; БуферДвоичныхДанных = Новый БуферДвоичныхДанных(ПоБайтныйМассив.Количество()); Для Счетчик = 0 По ПоБайтныйМассив.Количество() 1 Цикл БуферДвоичныхДанных[Счетчик] = ПоБайтныйМассив[Счетчик]; КонецЦикла; Возврат БуферДвоичныхДанных; КонецФункции Функция СобратьЧислоИзБайтов(БуферДвоичныхДанных) Результат = 0; Позиция = 0; Для каждого Байт Из БуферДвоичныхДанных Цикл Результат = Результат + ?(Позиция = 0, БуферДвоичныхДанных[Позиция], БуферДвоичныхДанных[Позиция] * Pow(256, Позиция)); Позиция = Позиция + 1; КонецЦикла; Возврат Результат; КонецФункции
Тогда операция логического «И» для двух чисел будет выглядеть следующим образом:
Копировать в буфер обменаФункция ЛогическоеИ(Знач Операнд1, Знач Операнд2, Знач ДлинаОперандов = 0) Буфер1 = РазбитьЧислоНаБайты(Операнд1); ДлинаОперандов = ?(ДлинаОперандов = 0, Буфер1.Размер, ДлинаОперандов); Буфер2 = РазбитьЧислоНаБайты(Операнд2, ДлинаОперандов); Буфер1.ЗаписатьПобитовоеИ(0, Буфер2, ДлинаОперандов); Результат = СобратьЧислоИзБайтов(Буфер1); Возврат Результат; КонецФункции
В данном случае принимается, что размер второго операнда в операции всегда равен размеру первого операнда.
Собственно использование данной функции (ЛогическоеИ()) можно проиллюстрировать следующим примером:
Копировать в буфер обменаЧТЕНИЕ = ЧислоИзДвоичнойСтроки("0b00000001"); СОЗДАНИЕ = ЧислоИзДвоичнойСтроки("0b00000010"); ИЗМЕНЕНИЕ = ЧислоИзДвоичнойСтроки("0b00000100"); УДАЛЕНИЕ = ЧислоИзДвоичнойСтроки("0b00001000"); ФлагиДоступа = ЧислоИзДвоичнойСтроки("0b00001110"); ЧтениеВозможно = ЛогическоеИ(ФлагиДоступа, ЧТЕНИЕ); ЗаписьВозможна = ЛогическоеИ(ФлагиДоступа, СОЗДАНИЕ+ИЗМЕНЕНИЕ); ПолныйДоступ = ЛогическоеИ(ФлагиДоступа, СОЗДАНИЕ+ИЗМЕНЕНИЕ+ЧТЕНИЕ+УДАЛЕНИЕ);
В данном примере рассматривается некий байт, который содержит флаги, определяющие возможности работы с предполагаемым файлом. Каждый флаг определяется своей маской (переменные ЧТЕНИЕ, СОЗДАНИЕ, ИЗМЕНЕНИЕ, УДАЛЕНИЕ). Проверка флага осуществляется просто: если результат функции ЛогическоеИ() не равен сумме проверяемых флагов ‑ значит, что флаги, которые необходимо проверить, установлены не все. Если результат функции равен сумме проверяемых флагов ‑ значит все проверяемые флаги установлены. В упрощенном случае проверка одного флага вернет значение 0, если флаг не установлен и собственно значение флага ‑ если флаг установлен.
В вышеприведенном примере для флагов ФлагиДоступа:
● Чтение недоступно. Значение переменной ЧтениеДоступно равно 0.
● Запись возможна. Значение переменной ЗаписьВозможна равно 6 (сумма флагов СОЗДАНИЕ и ИЗМЕНЕНИЕ).
● Полный доступ невозможен. Значение переменно ПолныйДоступ равно 15 (сумма всех описанных флагов, за вычетом недоступного флага ЧТЕНИЕ).
Аналогично методу ЛогическоеИ() можно выполнить и остальные функции побитовой работы с данными.
16.4. Работа с форматом PDF
16.4.1. Общая информация
Система «1С:Предприятие» позволяет сохранять табличный документ и пакет отображаемых документов в формате PDF. Поддерживается сохранение в формат PDF (формат описывается стандартом ISO 32000-1) и для табличного документа, и для пакета отображаемых документов. Для табличного документа также предоставляется возможность сохранения в один из форматов долгосрочного хранения PDF/A-1 (стандарт ISO 19005-1), PDF/A-2 (стандарт ISO 19005-2) и PDF/A-3 (стандарт ISO 19005-3).
При формировании документа в форматах семейства PDF/A следует помнить о следующих особенностях:
● Документ записывается в формате PDF 1.4 (только для формата PDF/A-1).
● Не поддерживается двусторонняя печать.
● При печати не поддерживается выбор источник бумаги по размеру страницы PDF.
● Документ не поддерживает прозрачность (только для формата PDF/A-1).
● При записи сохраняется информация о версии платформы, используемой для записи табличного документа, и даты создания документа.
Для того чтобы выполнить сохранение в формате PDF достаточно корректно указать параметры:
● ТипФайлаТаблицы метода ТабличныйДокумент.Записать().
● ТипФайла метода ПакетОтображаемыхДокументов.Записать().
Кроме собственно сохранения в формат PDF, платформа также поддерживает возможность работать с вложениями PDF-документов. Такая необходимость может возникнуть в том случае, если требуется документ, который одновременно содержит и человекочитаемую часть и машиночитаемую часть. В качестве примера можно рассмотреть, например, накладную, которая, будучи сохраненной в формат PDF, может быть просмотрена и распечатана (в своей человекочитаемой части) и одновременно, во вложении к этому документу PDF, есть, например, XML-файл, описывающий содержательную часть накладной, который пригоден для чтения каким-либо программным комплексом.
Программный интерфейс работы с PDF-документами предоставляет возможность работать с электронными подписями, которыми подписан документ. Предоставляется возможность проверки и установки ограничений документа, добавления новой подписи, проверки существующих подписей, размещения представления электронной подписи на странице документа. Надо понимать, что документ в формате PDF, который подписан ЭЦП, нельзя изменить, сохранив действительность подписи. Это касается не только самого содержимого документа, но и вложений к нему.
Существующая модель работы с документами в формате PDF не предоставляет возможности выполнять программное изменение человекочитаемой части содержимого.
16.4.2. Вложения
16.4.2.1. Запись вложений
Запись вложений PDF-документа выполняется с помощью объекта ЗаписьPDF. Работа с объектом доступна на клиенте и на сервере, доступна как асинхронная, так и синхронные способы работы. В документации будут рассматриваться примеры, использующие синхронные способы работы (для сокращения размера примеров). Добавление вложений поддерживается для PDF-документов версии 1.5 и последующих.
В общем, процесс добавления вложений выглядит следующим образом:
1. Создается объект ЗаписьPDF на основании файла или потока. Если объект создается без указания источника ‑ необходимо дополнительно открыть документ PDF методом ЗаписьPDF.Открыть().
2. Формируется коллекция вложений PDF-документа. Для формирования коллекции вложений предназначен объект КоллекцияВложенийPDF. Для добавления вложений предназначен метод КоллекцияВложенийPDF.Добавить().
3. Коллекция вложений добавляется в открытый документ с помощью метода ЗаписьPDF.ЗаписатьВложения().
4. PDF-документ закрывается методом ЗаписьPDF.Закрыть().
При записи происходит замещение вложений, для которых совпадают имена вложений в файле и записываемой коллекции. Под «именем» понимается значение свойства ВложениеPDF.ИмяФайла (элемент коллекции КоллекцияВложенийPDF). Если из коллекции вложений удален какой-то объект, то это не означает, что на уровне файла также будет выполнено «удаление» объекта. Платформа «1С:Предприятие» не выполняет поэлементное сравнение списков вложений (список существующих вложений и список записываемых вложений).
Если надо «удалить» вложение из файла ‑ необходимо воспользоваться методом ЗаписьPDF.ЗаписатьУдалениеВложений(). В этом случае сопоставление объектов также выполняется по именам: каждый элемент массива является значением свойства ВложениеPDF.ИмяФайла. При этом нужно понимать, что физического удаления вложений не выполняется. Вложение только отмечается как «удаленное». Но программа просмотра PDF-файлов позволяет вернуться к такому «удаленному» вложению.
16.4.2.2. Чтение вложений
Чтение вложений PDF-документа выполняется с помощью объекта ЧтениеPDF. Работа с объектом доступна на клиенте и на сервере, доступна как асинхронная, так и синхронные способы работы. В документации будут рассматриваться примеры, использующие синхронные способы работы (для сокращения размера примеров).
В общем, процесс чтения вложений выглядит следующим образом:
1. Создается объект ЧтениеPDF на основании файла или потока. Если объект создается без указания источника ‑ необходимо дополнительно открыть документ PDF методом ЧтениеPDF.Открыть().
2. Выполняется получение коллекции вложений из PDF-документа с помощью метода ЧтениеPDF.ПолучитьВложения().
3. PDF-документ закрывается методом ЧтениеPDF.Закрыть().
Коллекция вложений может существовать отдельно от объекта ЧтениеPDF. Таким образом, если необходимо выполнить «удаление» некоторых вложений у существующего файла PDF, это можно сделать следующим образом:
1. Открыть существующий документ PDF: создать объект конструктором или с помощью метода ЧтениеPDF.Открыть().
2. Получить вложения с помощью метода ЧтениеPDF.ПолучитьВложения().
3. Закрыть объект ЧтениеPDF с помощью метода ЧтениеPDF.Закрыть().
4. Использовать полученную коллекцию КоллекцияВложенийPDF для обновления этого же документа PDF с помощью объекта ЗаписьPDF.
16.4.3. Электронная цифровая подпись
16.4.3.1. Общая информация
Определение терминов, используемых в данном разделе, см. здесь. При необходимости некоторые термины будут уточняться или расширяться. В частности, в данном разделе, говоря об ЭЦП, мы будем считать, что электронная подпись состоит из двух частей: ЭЦП в криптографическом смысле (последовательность данных, полученная в результате криптографического преобразования исходной информации) и представление электронной подписи в виде графического изображения.
ЭЦП (в приложении к PDF-документу) может быть двух видов: утверждающая подпись и сертифицирующая подпись. Утверждающих электронных подписей может быть несколько в одном документе. При подписании документа утверждающей электронной подписью имеется возможность заблокировать любые изменения документа в дальнейшем. Сертифицирующая электронная подпись в документе всегда существует в единственном экземпляре. Но имеется возможность разрешить вносить в документ изменения, не изменяющие суть этого документа (например, добавлять комментарии).
Использование утверждающей подписи может выглядеть следующим образом:
● Формируется документ, например, коммерческое предложение.
● Автор коммерческого предложения подписывает его своей утверждающей подписью, тем самым фиксируя содержимое и подтверждая авторство.
● Затем предложение отправляется коммерческому директору фирмы, который проверяет корректность выставленного предложения и подписывает документ утверждающей подписью, при этом выставляя блокировку документа.
● Теперь коммерческое предложение утверждено ответственными лицами (путем электронной подписи) и заблокировано от изменений.
Использование сертифицирующей подписи может выглядеть следующим образом:
● Есть документ, который необходимо отправить на рецензирование по открытым каналам связи.
● Автор формирует документ и подписывает его сертифицирующей цифровой подписью, разрешая при этом выполнять рецензирование документа.
● Рецензент читает документ, комментирует его и отправляет назад. Обе стороны уверены, что документ остался неизменным (подпись это гарантирует).
Следует отметить, что приведенная в данном разделе классификация электронных подписей связана исключительно с документами PDF и определяется форматом PDF. При этом юридические определения электронной подписи и ее классификация (данные в соответствующих нормативных актах) никак, в общем случае, не взаимодействуют с определениями, используемыми в данном разделе.
16.4.3.2. Добавление подписи
Для добавления электронной подписи задействуются два механизма: объекты работы с документом PDF и средства криптографии, предоставляемые платформой.
Для того чтобы подписать документ PDF, необходимо выполнить несколько простых действий. Вначале нужно создать объект, описывающий цифровую подпись: Подпись = Новый ОписаниеПодписиPDF();. Рассмотрим, для чего можно использовать свойства данного объекта.
Для любой подписи существенными являются следующие свойства:
● Сертификат ‑ данное свойство всегда должно быть заполнено ссылкой на сертификат открытого ключа, с помощью которого проверяется сформированная цифровая подпись.
● ТипПодписи ‑ указывает, какой ЭЦП будет подписан документ. В зависимости от типа подписи будет необходимо указать одно из следующих свойства.
● БлокировкаДокумента ‑ в том случае, если свойство ТипПодписи установлено в значение Утверждающая, с помощью данного свойства (установив его в значение Истина) можно запретить любые модификации документа после подписания. Для утверждающей цифровой подписи установка свойства РазрешенияДоступаИзменения не имеет смысла.
● РазрешенияДоступаИзменения ‑ в том случае, если свойство ТипПодписи установлено в значение Сертифицирующая, с помощью данного свойства можно указать, какие действия пользователь может выполнять с подписанным документом. В любом случае, выполняемые действия не могу изменить суть подписанного документа. Для сертифицирующей цифровой подписи установка свойства БлокировкаДокумента не имеет смысла.
Если во время подписания будет использоваться графическое представление электронной подписи, то размещением этого изображения в документе управляются следующие свойства:
● НомерСтраницы ‑ на какой странице документа PDF будет впечатано графическое представление электронной подписи. Страницы нумеруются с 1.
● Лево и Верх ‑ эти свойства указывают отступы от левой и верхней границ (соответственно) страницы, где будет располагаться графическое изображение электронной подписи. Начало координат находится в левом верхнем углу страницы. Значения отступа задаются в миллиметрах.
● Ширина и Высота ‑ эти свойства указывают горизонтальный и вертикальный (соответственно) размер графического представления цифровой подписи.
После того, как объект, описывающий цифровую подпись, создан и заполнен данными, можно добавлять эту подпись к документу PDF.
Для добавления электронной подписи необходимо выполнить следующие действия:
● Создать объект доступа к криптографическому функционалу.
● Получить сертификат, который будет использоваться для проверки ЭЦП, как отдельный объект СертификатКриптографии.
● Создать и заполнить объект ОписаниеПодписиPDF.
● Создать объект, позволяющий добавлять ЭЦП к документу PDF (ЗаписьPDF).
● Подписать документ PDF методом ЗаписьPDF.ЗаписатьПодпись().
Пример подписи документа:
Копировать в буфер обменаТабличныйДокументДоговор.Записать(ИмяДокумента, ТипФайлаТабличногоДокумента.PDF); МенеджерКриптографии = Новый МенеджерКриптографии("", "", 80); МенеджерКриптографии.ПарольДоступаКЗакрытомуКлючу = Пароль; Сертификат = ПолучитьОткрытыйСертификат(); Штамп = ПолучитьМакет("ПодписьЛПР"); ПодписьPDF = Новый ОписаниеПодписиPDF; ПодписьPDF.Имя = "ПодписьДоговора"; ПодписьPDF.Сертификат = Сертификат; ПодписьPDF.ТипПодписи = ТипПодписиPDF.Утверждающая; ПодписьPDF.БлокировкаДокумента = Истина; ЗаписьPDF = Новый ЗаписьPDF(ИмяДокумента); ЗаписьPDF.ЗаписатьПодпись(МенеджерКриптографии, ПодписьPDF, Штамп);
Смотри также:
● Работа с криптографией (см. здесь).
16.4.3.3. Проверка подписи
Для подписанного документа PDF предоставляется две возможности:
● Получить список всех ЭЦП, которыми подписан документ, для анализа.
● Проверить действительность всех или одной, выбранной, ЭЦП.
Изменить любую цифровую подпись, которой уже подписан документ ‑ невозможно.
Для того чтобы получить список подписей, которыми подписан документ, следует использовать функцию ЧтениеPDF.ПолучитьОписанияПодписей(). Функция возвращает массив объектов описания подписи.
Копировать в буфер обменаМенеджерКриптографии = Новый МенеджерКриптографии("", "", 80); МенеджерКриптографии.ПарольДоступаКЗакрытомуКлючу = Пароль; ЧтениеPDF = Новый ЧтениеPDF(ИмяДокумента); Подписи = ЧтениеPDF.ПолучитьОписанияПодписей(МенеджерКриптографии);
Однако основная задача, которая возлагается на ЭЦП ‑ убедиться в том, что документ не был изменен с момента подписи. Для этого предназначены два метода:
1. Проверить действительность сразу всех ЭЦП, которыми подписан документ. Это можно сделать с помощью метода ЧтениеPDF.ПроверитьПодписи().
Пример проверки всех подписей:
Копировать в буфер обменаМенеджерКриптографии = Новый МенеджерКриптографии("", "", 80); МенеджерКриптографии.ПарольДоступаКЗакрытомуКлючу = Пароль; ЧтениеPDF = Новый ЧтениеPDF(ИмяДокумента); Попытка ЧтениеPDF.ПроверитьПодписи(МенеджерКриптографии); Исключение Сообщить("Подписи недействительны!"); КонецПопытки;
2. Проверить действительность конкретной ЭЦП, которой подписан документ. Это можно сделать с помощью метода ЧтениеPDF.ПроверитьПодпись().
Пример проверки подписей по одной:
Копировать в буфер обменаМенеджерКриптографии = Новый МенеджерКриптографии("", "", 80); МенеджерКриптографии.ПарольДоступаКЗакрытомуКлючу = Пароль; ЧтениеPDF = Новый ЧтениеPDF(ИмяДокумента); Подписи = ЧтениеPDF.ПолучитьОписанияПодписей(МенеджерКриптографии); Для каждого ТекПодпись из Подписи Цикл Попытка ЧтениеPDF.ПроверитьПодпись(ТекПодпись.Имя, МенеджерКриптографии); Исключение Сообщить("Подпись " + ТекПодпись.Имя + " не действительна !"); КонецПопытки; КонецЦикла;
16.4.3.4. Добавление представления подписи
Кроме непосредственно подписи документа PDF, платформа предоставляет возможность внедрить в документ представление подписи. Представлением подписи могут выступать объекты Картинка или ТабличныйДокумент.
Для представления подписи необходимо использовать объект типа ОписаниеОтображаемогоОбъектаPDF. Представление подписи указывается с помощью свойства данного типа Объект. Это единственное обязательное для заполнения свойство. Остальные свойства имеют следующее предназначение:
● Имя ‑ имя добавляемого представления.
● НомерСтраницы ‑ номер страницы, на которую будет добавлено представление подписи. Номера начинаются с 1. Если номер не указан ‑ объект будет добавлен на последнюю страницу документа.
● Лево, Верх ‑ определяет координаты левого верхнего угла добавляемого объекта на странице документа. Координаты задаются в миллиметрах, относительно левого верхнего угла страницы. Если эти значения не указаны, система будет выбирать эти значения автоматически.
● Ширина, Высота ‑ описывают размер добавляемого объекта на странице, в миллиметрах. Если эти значения не указаны, система будет выбирать эти значения автоматически.
Для того, чтобы добавить созданное описание отображаемого объекта в PDF-документ, необходимо использовать метод ЗаписьPDF.ЗаписатьОтображаемыйОбъект(). Таким образом, код, который добавляет некоторую картинку в PDF-документ, может выглядеть следующим образом:
Копировать в буфер обменаТабДок = СформироватьТабличныйДокумент(); ТабДок.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.PDF); Описание = Новый ОписаниеОтображаемогоОбъектаPDF; Описание.Имя = "ПодписьСотрудника"; Описание.Объект = БиблиотекаКартинок.Подпись; ЗаписьPDF = Новый ЗаписьPDF(имяФайла); ЗаписьPDF.ЗаписатьОтображаемыйОбъект(Описание);
16.4.4. Документ PDF
16.4.4.1. Общая информация
В предыдущих разделах были рассмотрены методы работы с документом PDF для выполнения «атомарных» действий: запись/чтение вложений и подпись документа/проверка подписи. В некоторых случаях может потребоваться выполнять эти действия не «по кусочкам», а все сразу, для одного документа. И, например, сразу напечатать PDF-документ. Для того, чтобы выполнить все эти действия, предназначен объект ДокументPDF. Этот объект доступен как на стороне клиентского приложения, так и на стороне сервера системы «1С:Предприятие». На стороне клиентского приложения доступны только асинхронные операции (имя метода заканчивается суффиксом Асинх), а на стороне сервер доступны только синхронные варианты методов. Также следует отметить, что при работе в веб-клиенте требуется установка расширения работы с файлами (для использования методов ПрочитатьАсинх(), ЗаписатьАсинх(), ДобавитьПодписьАсинх() и ДобавитьОтображаемыйОбъектАсинх()).
Таким образом, объект ДокументPDF позволяет выполнить следующие действия:
● Получить тип документа и список его вложений.
● Добавить/удалить вложение, очистить список вложений.
● Работать с наборами цифровых подписей документа и списком отображаемых объектов.
● Выполнить печать PDF-документа.
В данном разделе будут более подробно рассмотрены некоторые аспекты использования объекта ДокументPDF.
16.4.4.2. Чтение и запись документа
Для того, чтобы прочитать PDF-документ, необходимо использовать метод ДокументPDF.Прочитать(). Например, таким образом:
Копировать в буфер обменаДокумент = Новый ДокументPDF; Ждать Документ.ПрочитатьАсинх(ПолныйПутьКДокументу); Сообщить("Тип документа - " + Документ.ТипФайла); Сообщить("Вложения - " + Документ.Вложения.Количество());
Свойство ТипФайла позволит понять, в каком формате записан открытый документ (обычный или какой-то из форматов долгосрочного хранения PDF/A). Свойство Вложения предоставляет доступ к вложениям, которые существуют у открытого документа.
Открывать файл можно не только с диска, но и из потока. В этом случае отличается только значение первого параметра методов Прочитать()/ПрочитатьАсинх().
Документы PDF могут быть защищены паролем. Пароль можно передать в качестве значения второго параметра методов Прочитать()/ПрочитатьАсинх(). Для того, чтобы проверить наличие пароля у документа PDF, предназначены методы глобального контекста ДокументЗащищенПаролем()/ДокументЗащищенПаролемАсинх(). Эти методы вернут Истина, если документ защищен паролем. Сам пароль, очевидно, узнать таким образом не получится. Проверка может быть выполнена для файла и для документа, размещенного в потоке.
После того, как модификация документа PDF завершилась, можно записать документ в файл на диске или поток. Это можно выполнить с помощью методов Записать()/ЗаписатьАсинх(). При записи можно установить пароль (второй параметр методов). Также имеется возможность указать (с помощью третьего параметра методов), в каком формате записывать документ PDF: обычном или в одном из форматов долгосрочного хранения PDF/A.
16.4.4.3. Работа с вложениями
Для работы с вложениями открытого PDF-документа предназначено свойство Вложения. Это свойство является коллекцией вложений (тип значения КоллекцияВложенийPDF). При работе с вложениями можно выполнять следующие действия:
● Добавить вложение в коллекцию: Добавить().
● Получить вложение из коллекции: Получить().
● Удалить вложение: Удалить().
● Очистить коллекцию: Очистить().
● Найти вложение по имени файла: Найти().
● Получить параметры коллекции: Количество().
Схема работы с данной коллекцией не отличается от аналогичных коллекций объектов.
16.4.4.4. Страницы документа
Объект ДокументPDF содержит коллекцию Страницы, которая содержит информацию о каждой странице, входящей в состав документа (без возможности корректировки содержимого). Свойство является коллекцией типа КоллеккцияСтраницPDF. Элементом коллекция является объект СтраницаPDF. С помощью данного объекта можно получить следующие параметры страницы документа:
|
Свойство |
Описание |
|
Высота |
Высота страницы, в миллиметрах. |
|
Номер |
Номер текущей страницы документа. Нумерация начинается с 1. |
|
Ориентация |
Содержит угол (в градусах), на который повернута страница документа. |
|
ПолеСверху |
Содержит величину верхнего поля страницы, в миллиметрах. |
|
ПолеСлева |
Содержит величину левого поля страницы, в миллиметрах. |
|
ПолеСнизу |
Содержит величину нижнего поля страницы, в миллиметрах. |
|
ПолеСправа |
Содержит величину правого поля страницы, в миллиметрах. |
|
Ширина |
Ширина страницы, в миллиметрах. |
С помощью объекта СтраницаPDF нельзя установить свойства страницы. Они доступны только для чтения.
При размещении на страницах PDF-документа подписей (методы ДобавитьПодпись()/ДобавитьПодписьАсинх()) или объектов (методы ДобавитьОтображаемыйОбъект()/ДобавитьОтображаемыйОбъектАсинх()) имеется возможность учитывать ориентацию страниц документа.
Несколько поясним параметр, позволяющий учитывать ориентацию страницы. Параметры высоты и ширины страницы, которые находятся в одноименных свойствах объекта СтраницаPDF, не учитывают значение свойства Ориентация. Другими словами, если страница имеет формат А4 (портрет) и повернута на 90 градусов, то свойства объекта СтраницаPDF будут иметь такие значения: Высота равна 297, Ширина равна 210, а Ориентация равна 90. Если какой-либо объект добавляется на страницу в конкретное место, то есть два варианта решения проблемы: самостоятельно выполнять все пересчеты при размещении объекта на странице или размещать объект исходя из стандартного размера (высота и ширина), а весь пересчет отдать платформе, которая будет его выполнять, если параметр УчитыватьОриентациюСтраницы установлен в значение Истина. Таким образом, установка параметра УчитыватьОриентациюСтраницы позволяет всегда размещать объект там, где требуется разработчику, без дополнительных расчетов
16.4.4.5. Работа с подписями и отображаемыми объектами
После того, как документ PDF прочитан, объект ДокументPDF предоставляет возможность работы с цифровыми подписями и отображаемыми объектами. Основные подходы по работе с цифровыми подписями документа см. здесь. При работе с объектом типа ДокументPDF основная разница заключается в том, что все манипуляции с подписями можно проделать через сам документ, не прибегая к открытию PDF-документа отдельно на чтение и отдельно на запись.
Для того, чтобы получить список подписей используемого документа, следует использовать программный код, аналогичный следующему:
Копировать в буфер обменаДокумент = Новый ДокументPDF; Ждать Документ.ПрочитатьАсинх(ПолныйПутьКДокументу); МенеджерКрипто = Новый МенеджерКриптографии("", "", 80); МенеджерКрипто.ПарольДоступаКЗакрытомуКлючу = ПарольЗакрытогоКлюча; Подписи = Ждать Документ.ПолучитьОписанияПодписейАсинх(МенеджерКрипто);
После выполнения примера, переменная Подписи будет содержать массив с подписями открытого документа (значения типа ОписаниеПодписиPDF).
Для того, чтобы выполнить проверку подписей документа, есть две возможности:
● Проверить подписи по одной, используя метод ДокументPDF.ПроверитьПодпись() (или асинхронный аналог).
● Проверить все подписи, которые заданы для документа, с помощью метода ДокументPDF.ПроверитьПодписи() (или асинхронный аналог).
Проверка подписей выглядит следующим образом:
Копировать в буфер обменаСертификаты = Неопределено; Сертификаты = Ждать Документ.ПроверитьПодписиАсинх(МенеджерКрипто);
В переменной Сертификаты будет содержаться список сертификатов, которые использовались для подписи документа. Если проверить подписи невозможно ‑ будет сформировано исключение. Для добавления подписи необходимо использовать объект ОписаниеПодписиPDF и метода ДокументPDF.ДобавитьПодпись() (или асинхронный аналог).
Добавление отображаемого объекта в PDF-документ выполняется с помощью метода ДокументPDF.ДобавитьОтображаемыйОбъект() (или асинхронный аналог) и объекта ОписаниеОтображаемогоОбъектаPDF. В качестве примера можно рассмотреть добавление отображения подписи в документ. Место добавления и размер добавляемой картинки зависят от формата страницы документа (размера бумаги):
Копировать в буфер обменаДокумент = Новый ДокументPDF; Документ.Прочитать(ПолныйПутьКДокументу); КоличествоСтраниц = Документ.Страницы.Количество(); Если КоличествоСтраниц > 0 Тогда ИндексПоследнейСтраницы = КоличествоСтраниц - 1; ПоследняяСтраница = Документ.Страницы.Получить(ИндексПоследнейСтраницы); ОтобрОбъект = Новый ОписаниеОтображаемогоОбъектаPDF; ОтобрОбъект.Имя = "Подпись"; ОтобрОбъект.НомерСтраницы = ИндексПоследнейСтраницы + 1; ОтобрОбъект.Лево = ПоследняяСтраница.ПолеСлева; Если ПоследняяСтраница.Ширина = 297 И ПоследняяСтраница.Высота = 210 Или ПоследняяСтраница.Ширина = 210 И ПоследняяСтраница.Высота = 297 Тогда // Страница A4 // Берем большую картинку ОтобрОбъект.Объект = БиблиотекаКартинок.ПодписьБольшая; ОтобрОбъект.Верх = ПоследняяСтраница.Высота - ПоследняяСтраница.ПолеСнизу - 100; ИначеЕсли ПоследняяСтраница.Ширина = 210 И ПоследняяСтраница.Высота = 148 Или ПоследняяСтраница.Ширина = 148 И ПоследняяСтраница.Высота = 210 Тогда // Страница A5 // Берем среднюю картинку ОтобрОбъект.Объект = БиблиотекаКартинок.ПодписьСредняя; ОтобрОбъект.Верх = ПоследняяСтраница.Высота - ПоследняяСтраница.ПолеСнизу - 50; Иначе // Для остальных страниц берем маленькую картинку ОтобрОбъект.Объект = БиблиотекаКартинок.ПодписьМалая; ОтобрОбъект.Верх = ПоследняяСтраница.Высота - ПоследняяСтраница.ПолеСнизу - 25; КонецЕсли; Документ.ДобавитьОтображаемыйОбъект(ОтобрОбъект); Документ.Записать(ПолныйПутьКДокументу); КонецЕсли;
После того, как в прочитанный документ PDF внесены изменения ‑ документ необходимо записать на диск. В противном случае внесенные изменения будут потеряны.
16.4.4.6. Просмотр и печать документа
С помощью объекта ДокументPDF можно показать прочитанный документ пользователю и распечатать документ.
Для отображения PDF-документа в отдельной форме используется метод Показать(). Метод не применим к реквизиту формы клиентского приложения типа ДокументPDF.
Для печати документа предназначен метод Напечатать(). Перед печатью предоставляется возможность открыть диалог параметров печати документа. Также можно получить количество страниц прочитанного документа с помощью метода КоличествоСтраниц().
16.4.5. Прочие возможности
Система «1С:Предприятие» предоставляется возможность «убрать» цвет из текущего документа. В результате преобразования в документе все цветные фрагменты станут отображаться в оттенках серого. Документ станет максимально приближен к виду, который получается при распечатке цветного документа на черно-белом принтере. Эта возможность доступна с помощью метода ПреобразоватьВОттенкиСерого() объектов ЗаписьPDF и ДокументPDF.
16.5. Регулярные выражения
16.5.1. Общая информация
У вас есть проблема.
Вы решили использовать регулярные
выражения, чтобы её решить.
Теперь у вас две проблемы.
При работе с текстом постоянной задачей является поиск каких-либо вхождений одного текста в другой. Например, при анализе некоторого журнала нужно найти все строки, которые начинаются с текста «ОШИБКА». Это достаточно простой поиск. Другой вариант такого поиска ‑ найти в некотором тексте все слова, которые начинаются с заглавной буквы и имеют длину от 5 до 8 символов. Если первый вариант достаточно просто решается обычным перебором строк и использованием функции СтрНачинаетсяС() (или подобной функции из арсенала методов работы со строками), то второй вариант требует реализации более сложного алгоритма, который надо будет постоянно дорабатываться по мере усложнения условия или при смене условия поиска. Для упрощения сложного поиска информации в тексте был разработан специальный инструмент, который называется регулярные выражения.
Регулярные выражения (regular expressions, regexp) ‑ специальный язык, предназначенный для поиска информации в тексте, который основан на использовании метасимволов (wildcard characters). Поиск выполняется по специальному образцу (pattern), который называется шаблоном или маской. Шаблон состоит из обычных символов и метасимволов, которые определяют, какой текст должен быть найден. Другими словами ‑ шаблон определяет правила поиска.
Язык регулярных выражений является непростым языком. В то же время этот язык не имеет единого стандарта. Это привело к тому, что существует большое количество различных вариантов реализации регулярных выражений, которые называются диалектами. Данный раздел документации не ставит своей целью привести исчерпывающее описание регулярных выражений (любого диалекта). Данный раздел документации содержит краткое описание основных возможностей диалекта ICU (который, в свою очередь, основан на диалекте языка Perl, но расширен возможностями работы с символами Юникода). Этот диалект будет описан в силу того, что именно этот диалект используется платформой «1С:Предприятие». Отдельно повторим, что диалект, используемый в рамках системы «1С:Предприятие», полностью применим на текстах, содержащих символы Юникод без использования каких-либо дополнительных настроек.
Все примеры, которые приведены в этом разделе, не являются примерами решения реальных задач. Эти примеры приведены лишь для демонстрации того, как та или иная задача может быть решена с помощью регулярных выражений. Если необходимо изучить механизм регулярных выражений ‑ следует использовать специальную литературу. Данный раздел документации не подойдет для этого. Для получения полных знаний по регулярным выражениям могут оказаться полезными следующие источники:
● Фридл Дж. Регулярные выражения. 3-е издание. ‑ СПб.:Питер, 2018 (ISBN 978-5-4461-0646-2).
● Гойвертс Я., Левитан С. Регулярные выражения. Сборник рецептов. ‑ СПб.: Символ-Плюс, 2010 (ISBN 978-5-93286-181-3).
● https://unicode-org.github.io/icu/userguide/strings/regexp.html
Также стоит отметить, что постоянная практика применения механизма положительно сказывается на понимании регулярных выражений и границ их применимости (или не применимости).
Для отладки регулярных выражений можно использовать различные ресурсы сети Интернет (или программное обеспечение), например, сайт https://regex101.com/. Но это лишь один из имеющихся инструментов.
Смотри также:
● Язык выражений XPath (см. здесь).
● Оператор ПОДОБНО языка запросов (см. здесь).
16.5.2. Описание языка
Шаблон регулярного выражения состоит из строки, в которой присутствуют как обычные литералы, так и метасимволы. Кроме того, литералы и метасимволы можно объединять в символьные классы с помощью выражения […]. Также в регулярных выражениях будут участвовать операторы и флаги поиска. В разделе будет использоваться термин выражение, которое может состоять из одного:
● символа,
● метасимвола,
● символьного класса,
● другого выражения (подвыражения), если оно заключено в круглые скобки (…).
Используемый диалект регулярных выражений не поддерживает именованные группы в регулярном выражении.
Операторы регулярных выражений:
|
Оператор |
Описание оператора |
|
| |
Условие «ИЛИ». Совпадение одного из элементов, разделенных оператором. Каждое выражение, участвующее в операторе «ИЛИ» называется альтернативой. |
|
* |
Совпадение предыдущего выражения 0 или более раз. Результат будет содержать максимальное количество символов. |
|
+ |
Совпадение предыдущего выражения 1 или более раз. Результат будет содержать максимальное количество символов. |
|
? |
Совпадение предыдущего выражения 0 или 1 раз. Предпочтительнее 1 совпадение. |
|
{min} |
Совпадение предыдущего выражения ровно min раз. |
|
{min,} |
Совпадение с предыдущим выражением минимум min раз. Результат будет включать максимальное количество совпадений. |
|
{min,max} |
Совпадение с предыдущим выражением от min до max раз. Результат будет включать максимальное количество совпадений (но не более max). |
|
*? |
Совпадение предыдущего выражения 0 или более раз. Результат будет содержать минимальное количество символов. Ленивый оператор. |
|
+? |
Совпадение предыдущего выражения 1 или более раз. Результат будет содержать минимальное количество символов. Ленивый оператор. |
|
?? |
Совпадение предыдущего выражения 0 или 1 раз. Предпочтительнее 0 совпадений. Ленивый оператор. |
|
{min}? |
Совпадение предыдущего выражения ровно min раз. Ленивый оператор. |
|
{min,}? |
Совпадение не менее min раз, но не более, чем требуется для полного совпадения с выражением. Ленивый оператор. |
|
{min,max}? |
Совпадение с предыдущим выражением от min до max раз. Результат будет включать минимальное количество совпадений (но не менее min). Ленивый оператор. |
|
*+ |
Совпадение 0 или более раз. Захватывающий оператор. |
|
++ |
Совпадение 1 или более раз. Захватывающий оператор. |
|
?+ |
Совпадение ноль или один раз. Захватывающий оператор. |
|
{min}+ |
Совпадение предыдущего выражения ровно min раз. Захватывающий оператор. |
|
{min,}+ |
Совпадение с предыдущим выражением минимум min раз. Результат будет включать максимальное количество совпадений. Захватывающий оператор. |
|
{min,max}+ |
Совпадение с предыдущим выражением от min до max раз. Результат будет включать максимальное количество совпадений (но не более max). Захватывающий оператор. |
|
() |
Описывает сохраняющую группу захвата. |
|
(?: ...) |
Несохраняющая группа захвата. Выполняет группировку выражений, но не выполняет захват соответствующего текста. Для такой группы не может быть применен метасимвол обратной ссылки (\num). |
|
(?> ...) |
Атомарная группа. Атомарная группа является несохраняющей группой, которая дополнительно отвергает возвраты. |
|
(?# ...) |
Комментарий в произвольном формате (?#комментарий). Такая группа полностью игнорируется в процессе разбора выражения. |
|
(?= ...) |
Позитивная опережающая проверка. Этот оператор проверяет, совпадает ли текст, указанный в операторе, с текстом, расположенным непосредственно за позицией, где механизм регулярных выражений встретил оператор позитивной опережающей проверки. Позитивная опережающая проверка будет истинной, если шаблон в группе будет соответствовать тексту, который начинается с текущей позиции. Текущая позиция в тексте не меняется. |
|
(?! ...) |
Отрицательная опережающая проверка. Является обратной к позитивной опережающей проверке. Другими словами, этот оператор проверяет последующий текст на несоответствие регулярному выражению, указанном в операторе отрицательной опережающей проверки. |
|
(?<= ...) |
Позитивная ретроспективная проверка. Этот оператор проверяет, совпадает ли текст, указанный в операторе, с текстом, расположенным непосредственно перед позицией, где механизм регулярных выражений встретил оператор позитивной ретроспективной проверки. Текущая позиция в тексте не меняется. |
|
(?<! ...) |
Отрицательная ретроспективная проверка. Является обратной проверкой к позитивной ретроспективной проверке. Другими словами, этот оператор проверяет предшествующий текст на несоответствие регулярному выражению, указанном в операторе отрицательной ретроспективной проверки. |
|
(?ismwx-ismwx:...) |
Позволяет вычислить группу с заданным или не заданным значением флага (флагов). Выражение (?i:…) вычислит выражение «…» без учета регистра, при этом весь остальной шаблон будет учитывать регистр символов. |
|
(?ismwx-ismwx) |
Задание флагов. Изменение состояния флагов будет действовать на ту часть шаблона, которая следует за настройкой. Например, выражение (?i) выключает учет регистра символов для всей оставшейся части шаблона. Включить (задать) флаг: (?i), выключить флаг: (?-i). |
Далее приводится описание флагов, которые могут быть изменены при описании шаблона:
|
Флаг |
Описание |
|
i |
Если флаг задан, то сопоставление будет происходить без учета регистра. Аналогом данного флага является параметр ИгнорироватьРегистр методов работы с регулярными выражениями. |
|
x |
Если флаг задан, то в шаблонах и операторах (?#...) разрешено использовать пробелы. |
|
s |
Если флаг задан, то метасимвол «.» в шаблоне будет соответствовать разрыву строки (переводу строки) во входящем тексте. Если флаг не задан, то метасимвол «.» не соответствует разрыву строки во входящем тексте. Разрыв строки определяется аналогично оператору «$». |
|
m |
Управление поведением в шаблоне операторов «^» и «$». По умолчанию они будут совпадать только в начале и конце вводимого текста соответственно (флаг не задан). Если этот флаг задан, то операторы «^» и «$» также будут совпадать в начале и конце каждой строки во входящем тексте. Аналогом данного флага является параметр МногострочныйПоиск методов работы с регулярными выражениями. |
|
w |
Управляет поведением метасимвола \b в шаблоне. Если флаг задан, то границы слов находятся в соответствии с определениями слов, которое дано в «Unicode UAX 29, Text Boundaries». По умолчанию (флаг не задан) границы слов идентифицируются с помощью простой классификации символов как «слово» или «не слово», что приближается к традиционному поведению регулярных выражений. Результаты, полученные с помощью двух вариантов, могут отличаться при использовании пробелов и других символов, не являющихся «словами». |
В состав регулярного выражения могут входить следующие метасимволы:
|
Метасимвол |
Вне класса |
[В классе] |
Описание метасимвола |
|
\a |
+ |
+ |
Соответствует символу BELL (\u0007). |
|
\A |
+ |
|
Соответствует началу текста, в котором выполняется поиск. В отличие от оператора ^, метасимвол \A не соответствует началу новой строки. |
|
\b |
+ |
|
Соответствует, если текущая позиция является границей слова. Граница возникает при переходе между словом (метасимвол \w) и символом, который не является словом (метасимвол \W). |
|
\B |
+ |
|
Соответствует, если текущая позиция не является границей слова. |
|
\cX |
+ |
+ |
Соответствует символу, который образован сочетанием клавиш Ctrl + X (символ указывается в верхнем регистре). |
|
\d |
+ |
+ |
Соответствует любому символу из общей категории Юникода «Nd (Number, decimal digit)». Можно представить как \p{Nd} или (в упрощенном виде) как [0-9]. |
|
\D |
+ |
+ |
Соответствует символу, который не является десятичной цифрой. |
|
\e |
+ |
+ |
Соответствует символу ESCAPE (\u001B). |
|
\E |
+ |
+ |
Завершает последовательность экранируемых символов, которая начинается с метасимвола \Q. |
|
\f |
+ |
+ |
Соответствует символу FORM FEED (Символы.ПФ, \u000c). |
|
\G |
+ |
+ |
Совпадает с позицией, в которой завершилось предыдущее совпадение. |
|
\n |
+ |
+ |
Соответствует символу LINE FEED (Символы.ПС, \u000a). |
|
\N{UNICODE CHARACTER NAME} |
+ |
+ |
Соответствует именованному символу Юникода. |
|
\p{UNICODE PROPERTY NAME} |
+ |
+ |
Соответствует любому символу с указанным свойством Юникода (Unicode Property). Поддерживаются свойства не выше Unicode версии 6.0. |
|
\P{UNICODE PROPERTY NAME} |
+ |
+ |
Соответствует любому символу, не имеющего указанного свойства Юникода (Unicode Property). Поддерживаются свойства не выше Unicode версии 6.0. |
|
\Q |
+ |
+ |
Экранирует все последующие символы, пока не будет встречен метасимвол \E. |
|
\r |
+ |
+ |
Соответствует символу CARRIAGE RETURN (Символы.ВК, \u000d). |
|
\s |
+ |
+ |
Соответствует пробельному символу. Пробельный символ определяется как [\t\n\f\r\p{Z}]. |
|
\S |
+ |
+ |
Соответствует не пробельному символу. |
|
\t |
+ |
+ |
Соответствует символу горизонтальной табуляции (Символы.Таб, \u0009). |
|
\uhhhh |
+ |
+ |
Соответствует символу с 16-ричным кодом hhhh. |
|
\Uhhhhhhhh |
+ |
+ |
Соответствует символу с 16-ричным кодом hhhhhhhh. Необходимо указать ровно восемь шестнадцатеричных цифр. |
|
\w |
+ |
+ |
Соответствует символу, который может входить в состав слов. Такой символ описывается следующим символьным классом: [\p{Alphabetic}\p{M}\p{Nd}\p{Pc}\u200c\u200d]. |
|
\W |
+ |
+ |
Соответствует символу, который не может входить в состав слов (в терминах метасимвола \w). |
|
\x{hhhh} |
+ |
+ |
Соответствует символу с 16-ричным кодом hhhhhh. Может быть указано от 1 до 6 (включительно) 16-ричных цифры. |
|
\xhh |
+ |
+ |
Соответствует символу с 16-ричным кодом hh. |
|
\Z |
+ |
|
Соответствует в том случае, если текущая позиция соответствует концу обрабатываемого текста (не строки), но при этом находится перед терминатором последней строки, если таковой существует. |
|
\z |
+ |
|
Соответствует в том случае, если текущая позиция соответствует концу обрабатываемого текста (не строки). |
|
\num |
+ |
|
Обратная ссылка. Будет соответствовать группе захвата с номером num. num должно быть числом, которое больше 1 и не больше максимального количества групп захвата в шаблоне (но не больше 9). |
|
\0ooo |
+ |
+ |
Соответствует 8-ричному числу. ооо означает три 8-ричных цифры, которые не могут превышать значение 0377 (в 8-ричной системе). Ведущий ноль является обязательным и позволяет отличить 8-ричную константу от обратной ссылки. |
|
[pattern] |
+ |
+ |
Соответствует одному любому символу из символьного класса. |
|
. |
+ |
|
Соответствует любому символу. |
|
^ |
+ |
|
Соответствует началу строки. |
|
$ |
+ |
|
Соответствует концу строки. Символами конца строки выступают символы \u000a, \u000b, \u000c, \u000d, \u0085, \u2028, \u2029 и последовательность символов \u000d \u000a. |
|
\ |
+ |
|
Экранирует следующий символ (представляет следующий символ как литерал). Следующие символы должны быть экранированы, если требуется воспринимать их как литералы: *, ?, +, [, (, ), {, }, ^, $, |, \. |
|
\ |
|
+ |
Экранирует следующий символ (представляет следующий символ как литерал). Следующие символы должны быть экранированы, если требуется воспринимать их как литералы: [, ], \. В зависимости от контекста, следующие символы также могут потребовать экранирования: &. |
В данной таблице указано, как ведет себя тот или иной оператор в самом регулярном выражении и внутри символьного класса:
● Если «+» стоит в обеих колонках, то это значит, что метасимвол работает одинаково как в самом шаблоне, так и внутри класса. Например, метасимвол \d будет означать десятичную цифру как в регулярном выражении \d, так и в символьном классе [\d].
● Если «+» стоит только в одной из колонок, то это означает, что описание метасимвола действительно только для той части регулярного выражения, которая отмечена «+». Например, экранирование символов (метасимвол \) работает по-разному в символьном классе и вне его (класса), а метасимвол \b не может использовать в символьном классе.
Описание символьного класса […] означает, что в данном месте шаблона может находиться любой символ из перечисленных внутри квадратных скобок. Внутри описания символьного класса допускается использовать операторы ‑ и ^, а также метасимволы, которые применимы внутри символьного класса. Оператор ‑ внутри символьного класса означает, для выражения [a-z] ‑ «последовательность символов от a (латинская, строчная) до z (латинская строчная)». Оператор ^ означает что в данном месте должен располагаться символ, который не входит в символьный класс. Например, выражение [^0-9] означает, что в этом месте шаблона должен находиться любой символ, кроме десятичной цифры.
Операторы *, +, ?, {min, max} часто называют квантификаторами: операторами, которые определяют количество экземпляров повторяющегося элемента. Существует несколько вариантов квантификаторов, которые отличаются по способу выполнения шаблона и количеству символов, соответствующих шаблону поиска:
● Жадный квантификатор ‑ обеспечивает получение максимального количества символов, которое соответствует выражению, для которого применяется квантификатор. Другими словами, квантификатор повторяется столько раз, сколько это возможно. Жадные квантификаторы: *, +, ?, {min, max}.
● Ленивый квантификатор ‑ обеспечивает получение минимального количества символов, которое соответствует выражению, для которого применяется квантификатор. Квантификатор повторяется наименьшее количество раз, при этом допускается повтор поиска в той части строки, которая уже захвачена шаблоном (обратный проход). Ленивые квантификаторы: *?, +?, ??, {min, max}?.
● Захватывающий (или сверхжадный) квантификатор ‑ аналогичен жадному квантификатору, но не допускает повтор поиска в той части исходной строки, которая уже захвачена шаблоном. Захватывающие квантификаторы: *+, ++, ?+, {min, max}+.
16.5.3. Работа с регулярными выражениями из встроенного языка
Для работы с регулярными выражениями во встроенном языке предназначены несколько методов глобального контекста:
● СтрНайтиПоРегулярномуВыражению(). Данный метод выполняет последовательный поиск подстроки, которая соответствует шаблону поиска. Строка в которой выполняется поиск, а также шаблон поиска ‑ передаются параметрами метода. Также указывается направление поиска, начальная позиция поиска в строке и какое вхождение подстроки интересует в результате поиска.
● СтрНайтиВсеПоРегулярномуВыражению(). Данный метод позволяет получить все подстроки, соответствующие шаблону поиска. Строка, в которой выполняется поиск и шаблон поиска передаются параметрами. Результатом работы метода является массив объектов РезультатПоискаПоРегулярномуВыражению. Количество элементов массива соответствует количеству подстрок, соответствующих шаблону поиска.
● СтрЗаменитьПоРегулярномуВыражению(). В переданной строке метод выполняет замену подстроки, параметры которой задаются шаблоном поиска, на фиксированную строку, указанную параметром. При замене не поддерживается возможность указания строки замены в виде ссылок на группы поисковой строки.
● СтрПодобнаПоРегулярномуВыражению(). Метод проверяет, что переданная строка соответствует шаблону.
У методов работы с регулярными выражениями есть два необязательных параметра, отвечающих за регистрочувствительность и многострочность поиска. Эти же параметры можно изменять и в самом шаблоне поиска. При этом указание параметра в начале шаблона (например так: (?m)^…) эквивалентно указанию параметра метода (например так: СтрПодобнаПоРегулярномуВыражению(Где, Шаблон, , Истина)).
При выполнении поиска шаблона, метод СтрНайтиПоРегулярномуВыражению() возвращает результат поиска в виде объекта РезультатПоискаПоРегулярномуВыражению. Данный объект содержит несколько свойств:
● Собственно найденная строка (свойство Значение).
● Начальная позиция искомой подстроки (свойство НачальнаяПозиция).
● Длина найденной подстроки (свойство Длина).
Таким образом, поиск нескольких вхождений одной и той же подстроки можно реализовать с помощью этих свойств и параметров метода СтрНайтиПоРегулярномуВыражению(), по аналогии с обычным методом СтрНайти().
Если регулярное выражение содержит группы, то можно использовать метод РезультатПоискаПоРегулярномуВыражению.ПолучитьГруппы(). Результатом работы метода будет массив объектов типа ГруппаРезультатаПоискаПоРегулярномуВыражению. Каждый объект такого типа содержит следующие свойства:
● Собственно значение группы (свойство Значение).
● Начальная позиция текущей группы (свойство НачальнаяПозиция).
● Длина текста группы (свойство Длина).
Если регулярное выражение содержит вложенные группы, то группы будут находиться в массиве в следующем порядке: первая группа, затем все вложенные группы, затем вторая группа и т. д. Если вложенная группа, в свою очередь, содержит вложенные группы ‑ они будут выводиться «под» родительской группой. Порядок вывода групп ‑ всегда «слева-направо».
Если требуется в произвольном тексте, представленном в строке Источник, найти текст, соответствующим поисковому выражению Шаблон, можно использовать одним из следующих примеров программы на встроенном языке:
Пример последовательного поиска:
Копировать в буфер обменаПроцедура ВыполнитьПоиск(Источник, Шаблон) НачальныйСимвол = 1; Пока Истина Цикл Результат = СтрНайтиПоРегулярномуВыражению(Источник, Шаблон, , НачальныйСимвол); Если Результат.Длина = 0 Тогда Прервать; КонецЕсли; Сообщить("Результат = " + Результат.Значение); НачальныйСимвол = Результат.НачальнаяПозиция + Результат.Длина; Если НачальныйСимвол >= СтрДлина(Источник) Тогда Прервать; КонецЕсли; КонецЦикла; КонецПроцедуры
Пример поиска сразу всех вхождений:
Копировать в буфер обменаПроцедура ВыполнитьПоиск(Источник, Шаблон) Результаты = СтрНайтиВсеПоРегулярномуВыражению(Источник, Шаблон); Для каждого Результат Из Результаты Цикл Сообщить("Результат = " + Результат.Значение); КонецЦикла; КонецПроцедуры
Результат работы методов (с точки зрения результатов поиска) ‑ одинаков.
При работе с группами код может выглядеть следующим образом:
Копировать в буфер обменаИсточник = "© ООО "1С-Софт", 1996 - 2022" "Фирма "1С", Москва, 123056, а/я 64" "Отдел продаж: Селезневская ул., 21," "телефон: +7 (495) 737-92-57," "факс: 8 (495) 681-44-07"; Шаблон = "\+?(\d{1,3}?)?[ (.-]*(\d{3,4}?)[ ).-]*(\d{2,3})[ .-]?(\d{2})[ .-]?(\d{2})"; Результаты = СтрНайтиВсеПоРегулярномуВыражению(Источник, Шаблон); Для каждого Результат Из Результаты Цикл ГруппыПоиска = Результат.ПолучитьГруппы(); // индексы в массиве: // 0 - код страны (или 8) // 1 - код города // 2 - первый блок цифр номера // 3 - второй блок цифр номера // 4 - третий блок цифр номера КонецЦикла;
16.5.4. Примеры использования регулярных выражений
16.5.4.1. Общая информация
В данном разделе приводится один из возможных вариантов решения той или иной задачи. Все примеры не являются решениями реальных задач, а являются лишь демонстрацией возможностей механизма. При решении конкретной задачи могут быть разработаны другие варианты регулярных выражений.
Для упрощения примеров, в текстах будут приводиться исходные данные, само регулярное выражение и пример вывода. Сама процедура поиска будет взята из предыдущего раздела (любая из двух процедур ‑ последовательного поиска или поиска сразу всех вхождений) и будет использоваться под именем ВыполнитьПоиск() с параметрами Источник и Шаблон. Через формальный параметр Источник будет передавать текст, в котором нужно выполнить поиск, а формальный параметр Шаблон будет содержать поисковый шаблон. Результат поиска выводится в окно сообщений.
Таким образом, полный текст примера можно будет выразить следующим образом:
Копировать в буфер обменаПроцедура ПримерИспользования() Источник = "…"; // здесь должен быть текст, в котором будем искать Шаблон = "…"; // здесь должен быть шаблон поиска ВыполнитьПоиск(Источник, Шаблон); КонецПроцедуры
16.5.4.2. Поиск нужных версий в массиве текста
Представим, что у нас есть массив текста, который содержит номер версии системы «1С:Предприятие» и дата выпуска версии (в данном примере этот текст не важен). На необходимо извлечь из этого текста все версии, которые выглядят следующим образом: 8.3.20.20xx (где xx ‑ две десятичные цифры). Исходный текст (в котором будет нужно выполнить поиск) представлен следующим образом:
Копировать в буфер обменаИсточник = "8.3.22.1603 28.09.22 |8.3.18.1902 20.09.22 |8.3.21.1508 19.09.22 |8.3.20.2076 19.09.22 |8.3.19.1665 19.09.22 |8.3.19.1659 30.08.22 |8.3.18.1894 30.08.22 |8.3.17.2665 30.08.22 |8.3.21.1484 25.08.22 |8.3.20.2039 25.08.22 |8.3.20.1996 02.08.22 |8.3.21.1393 19.07.22 |8.3.20.1914 14.06.22 |8.3.20.1838 28.04.22";
Напомним, что символ «|» в многострочной строке «1С:Предприятие» означает новую строку. После номера версии располагается символ горизонтальной табуляции.
Используем шаблон: ^8\.3\.20\.20\d{2}\t.
Однако, в результате выполнения данного примера:
Копировать в буфер обменаПроцедура ПримерИспользования() Источник = "8.3.22.1603 28.09.22 |8.3.18.1902 20.09.22 |8.3.21.1508 19.09.22 |8.3.20.2076 19.09.22 |8.3.19.1665 19.09.22 |8.3.19.1659 30.08.22 |8.3.18.1894 30.08.22 |8.3.17.2665 30.08.22 |8.3.21.1484 25.08.22 |8.3.20.2039 25.08.22 |8.3.20.1996 02.08.22 |8.3.21.1393 19.07.22 |8.3.20.1914 14.06.22 |8.3.20.1838 28.04.22"; Шаблон = "8\.3\.20\.20\d{2}\t "; ВыполнитьПоиск(Источник, Шаблон); КонецПроцедуры
Результатов не будет. Это произошло потому, что по умолчанию, оператор ^ выдает совпадение только в самом начале строки поиска. А все переводы строки внутри поискового текста (переменная Источник) не будут соответствовать началу строки. Чтобы решить эту проблему, расширим наше выражение установкой параметра m, который позволит оператору ^ обрабатывать переносы строк внутри текста-источника, как новую строку.
Новый шаблон: (?m)^8\.3\.20\.20\d{2}\t.
Результат поиска:
Копировать в буфер обменаРезультат = 8.3.20.2076 Результат = 8.3.20.2039
16.5.4.3. Поиск всех дат в тексте
Теперь представим, что нам необходимо найти все даты выпуска всех версий в исходном тексте предыдущего примера. Дата задается в формате dd.mm.yy. Данное регулярное выражение не претендует на поиск дат в любых возможных форматах.
Используем шаблон: [0-3]\d\.[01]\d\.\d{2}.
Результат поиска:
Копировать в буфер обменаРезультат = 28.09.22 Результат = 20.09.22 Результат = 19.09.22 Результат = 19.09.22 Результат = 19.09.22 Результат = 30.08.22 Результат = 30.08.22 Результат = 30.08.22 Результат = 25.08.22 Результат = 25.08.22 Результат = 02.08.22 Результат = 19.07.22 Результат = 14.06.22 Результат = 28.04.22
16.5.4.4. Поиск даты выпуска нужных версий
Усложним предыдущий пример следующим образом: необходимо найти даты выпуска для версий, соответствующих нашему желанию: 8.3.20.20xx.
Для этого модифицируем шаблон поиска дат выпуска следующим образом: (?m)(?<=^8\.3\.20\.20\d{2}\t)[0-3]\d\.[01]\d\.\d{2}.
Результат поиска:
Копировать в буфер обменаРезультат = 25.08.22 Результат = 19.09.22
16.5.4.5. Получение даты выпуска всех версий
Возьмем ранее использованный текст, который содержит номер версии и дата выпуска версии и попробуем получить пару значений: версия ‑ дата выпуска. Для этого воспользуемся группировкой регулярных выражений:
Копировать в буфер обмена// Источник - массив "версия - дата" из предыдущих аналогичных примеров Шаблон = "^(8\.[0-3]\.\d{1,2}\.\d{1,4})\t([0-3]\d\.[01]\d\.\d{2})"; Результаты = СтрНайтиВсеПоРегулярномуВыражению(Источник, Шаблон, , Истина); Для каждого Результат Из Результаты Цикл ГруппыРезультата = Результат.ПолучитьГруппы(); МассивРезультатов = Новый Массив; Для каждого ГруппаРезультата Из ГруппыРезультата Цикл МассивРезультатов.Добавить(ГруппаРезультата.Значение); КонецЦикла; Сообщить(СтрСоединить(МассивРезультатов, " / ")); КонецЦикла;
Результат поиска:
Копировать в буфер обмена8.3.22.1603 / 28.09.22 8.3.18.1902 / 20.09.22 8.3.21.1508 / 19.09.22 8.3.20.2076 / 19.09.22 8.3.19.1665 / 19.09.22 8.3.19.1659 / 30.08.22 8.3.18.1894 / 30.08.22 8.3.17.2665 / 30.08.22 8.3.21.1484 / 25.08.22 8.3.20.2039 / 25.08.22 8.3.20.1996 / 02.08.22 8.3.21.1393 / 19.07.22 8.3.20.1914 / 14.06.22 8.3.20.1838 / 28.04.22
Стоит отметить, что каждая версия и дата представляет собой отдельную группу.
16.5.4.6. Фамилии с именами на определенную букву
Представим, что у нас есть список «Фамилия Имя» и нам необходимо выделить из этого списка только те фамилии, имена которых начинаются на определенную букву (например, «Д»).
Вот этот список (список вымышленный, все совпадения случайны):
Копировать в буфер обменаЛавров Егор, Герасимова София, Попова Мария, Чернов Владимир, Минаев Лев, Степанова Александра, Семенова Анна, Михайлов Михаил, Долгова Арина, Сергеева Диана, Колпакова Юлиана, Орлова Вера, Платонова Кира, Елисеев Илья, Борисов Максим, Афанасьева Екатерина, Евсеев Евгений, Богомолов Максим, Леонова Вероника, Никитин Николай, Булатова Александра, Журавлев Святослав, Морозов Валерий, Моисеев Лев, Шмелева Дарья
Используем шаблон: (\b\w+\b)(?=\s\bД).
Результат поиска:
Копировать в буфер обменаРезультат = Сергеева Результат = Шмелева
16.5.4.7. Проверка на номер телефона
В данном примере иллюстрируется проверка того, что в переданной строке находится только номер телефона и ничего более:
Копировать в буфер обменаИсточник = "+74957777777"; Шаблон = "^\+?(\d{1,3}?)?[ (.-]*(\d{3,4}?)[ ).-]*(\d{2,3})[ .-]?(\d{2})[ .-]?(\d{2})$"; Результат = СтрПодобнаПоРегулярномуВыражению(Источник, Шаблон); Если Результат Тогда Сообщить("Это телефон"); Иначе Сообщить("Это что-то другое"); КонецЕсли;
16.5.4.8. Получить номера телефонов из текста
Предположим, что у нас есть некоторый текст, в котором могут быть указаны номера телефонов. Нам необходимо получить эти номера.
Текст для поиска:
Копировать в буфер обмена© ООО "1С-Софт", 1996 - 2022 Фирма "1С", Москва, 123056, а/я 64 Отдел продаж: Селезневская ул., 21, телефон: +7 (495) 737-92-57 факс: +7 (495) 681-44-07
Используем шаблон: \+?(\d{1,3}?)?[ (.-]*(\d{3,4}?)[ ).-]*(\d{2,3})[ .-]?(\d{2})[ .-]?(\d{2}). Данный шаблон не сможет определить все возможные способы написания телефонов в России (и мире).
Результат поиска:
Копировать в буфер обменаРезультат = +7 (495) 737-92-57 Результат = +7 (495) 681-44-07
16.5.4.9. Проверка на URL
Данный пример иллюстрирует проверку того, что в переданной строке находится URL (по формальным признакам). Данный пример не будет пропускать URL, написанные символами, отличными от английского алфавита.
Копировать в буфер обменаИсточник = "https://www.1c.ru"; Шаблон = "(?i)^((https?|ftp)://|(www|ftp)\.)[a-z0-9]+(\.[a-z0-9]+)+([/?].*)?\Z"; Результат = СтрПодобнаПоРегулярномуВыражению(Источник, Шаблон); Если Результат Тогда Сообщить("Это URL"); Иначе Сообщить("Что-то другое"); КонецЕсли;
16.5.4.10. Получить URL из текста
Имеется некоторый текст, в котором есть один или несколько URL. Необходимо получить эти значения. Для поиска используется текст:
Копировать в буфер обмена© ООО "1С-Софт", 1996 - 2022 Фирма "1С", Москва, 123056, а/я 64 Отдел продаж: Селезневская ул., 21, телефон: +7 (495) 737-92-57, факс: +7 (495) 681-44-07, e-mail: 1c@1c.ru URL: https://www.1c.ru, https://www.v8.1c.ru, https://users.v8.1c.ru
Используем шаблон: (?i)\b(https?|ftp|file)://[-A-Z0-9+&@#/%?=~_|$!:,.;]*[A-Z0-9+&@#/%?=_|$].
Результат поиска:
Копировать в буфер обменаРезультат = https://www.1c.ru Результат = https://www.v8.1c.ru Результат = https://users.v8.1c.ru
16.5.4.11. Проверка корректности ввода адреса IPv4
Данный пример демонстрирует возможность проверки, что строка содержит корректный сетевой адрес в формате IPv4:
Копировать в буфер обменаИсточник = "192.168.1.10"; Шаблон = "^(?:(?:2(?:[0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9])\.){3}(?:(?:2([0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9]))$"; Результат = СтрПодобнаПоРегулярномуВыражению(Источник, Шаблон); Если Результат Тогда Сообщить("Это IPv4"); Иначе Сообщить("Что-то другое"); КонецЕсли;
16.5.4.12. Проверка корректности ввода адреса IPv6
Данный пример демонстрирует возможность проверки, что строка содержит корректный сетевой адрес в формате IPv6:
Копировать в буфер обменаИсточник = "1762::b03:127.32.67.15"; Шаблон = "(?i)^(?:(?:(?:[\dA-F]{1,4}:){6}|(?=(?:[\dA-F]{0,4}:){0,6}(?:\d{1,3}\.){3}\d{1,3}$)(([\dA-F]{1,4}:){0,5}|:)((:[\dA-F]{1,4}){1,5}:|:))(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[\dA-F]{1,4}:){7}[\dA-F]{1,4}|(?=(?:[\dA-F]{0,4}:){0,7}[\dA-F]{0,4}$)(([\dA-F]{1,4}:){1,7}|:)((:[\dA-F]{1,4}){1,7}|:))$"; Результат = СтрПодобнаПоРегулярномуВыражению(Источник, Шаблон); Если Результат Тогда Сообщить("Это IPv6"); Иначе Сообщить("Что-то другое"); КонецЕсли;
16.5.4.13. Получить из текста все слова, написанные кириллицей
Допустим, что у нас есть текст, который содержит вперемешку слова на кириллице и на других языках. Стоит задача извлечь все кириллические слова из текста.
Исходный текст: пример this интересный text.
Используем шаблон: \b\p{Cyrillic}+\b.
Результат поиска:
Копировать в буфер обменаРезультат = пример Результат = интересный
16.5.4.14. Чтение конфигурационного файла
Рассмотрим более сложный пример использования регулярных выражений. Допустим у нас есть некоторый файл, который содержит группы (некоторая последовательность символов, заключенная в «[]») и в каждой группе есть некоторый набор пар «идентификатор=значение». В строках могут быть комментарии, которые начинаются с символов «;» или «#». В любом файле должна быть хотя-бы одна группа. В таком формате часто создаются конфигурационные файлы, например, конфигурационный файл nethasp.ini платформы «1С:Предприятие». Другим примером такого файла можно назвать файл ODBC.INI из каталога %WINDIR%.
Предлагаемый метод будет считывать конфигурационный файл в соответствие. Один элемент соответствия будет содержать одну группу конфигурационного файла:
● Свойство соответствия Ключ будет содержать имя группы (без символов «[]»).
● Свойство соответствия Значение будет содержать все пары «идентификатор=значение» данной группы (в виде соответствия). Устройство соответствия значений аналогично:
● Свойство Ключ содержит идентификатор из файла.
● Свойство Значение содержит значение из файла.
Собственно код выглядит следующим образом (вначале идет пример вызова функции чтения):
Копировать в буфер обменаПараметры = ПрочитатьINIФайл("c:\windows\odbc.ini"); Функция ПрочитатьINIФайл(ПутьКФайлу) СодержимоеФайла = Новый Соответствие; ИмяГруппы = ""; СодержимоеГруппы = Неопределено; МаскаГруппы = "^\[(.*)\]$"; МаскаЗначения = "^([^\[\]\r\n#;]+)(?:[\s]*=[\s]*)([^#;\\\r\n]*(?:\\.[^#;\\\r\n]+)*)"; Чтение = Новый ЧтениеТекста(ПутьКФайлу); Пока Истина Цикл СтрокаФайла = Чтение.ПрочитатьСтроку(); Если СтрокаФайла = Неопределено Тогда Прервать; КонецЕсли; СтрокаФайла = СокрЛП(СтрокаФайла); ОписаниеГруппы = СтрНайтиВсеПоРегулярномуВыражению(СтрокаФайла, МаскаГруппы); Если ОписаниеГруппы.Количество() = 0 И СодержимоеГруппы = Неопределено Тогда Продолжить; ИначеЕсли ОписаниеГруппы.Количество() > 0 Тогда Если СодержимоеГруппы <> Неопределено Тогда СодержимоеФайла.Вставить(ИмяГруппы, СодержимоеГруппы); КонецЕсли; СодержимоеГруппы = Новый Соответствие; ИмяГруппы = СокрЛП(ОписаниеГруппы[0].ПолучитьГруппы()[0].Значение); Продолжить; КонецЕсли; ОписаниеЗначения = СтрНайтиВсеПоРегулярномуВыражению(СтрокаФайла, МаскаЗначения); Если ОписаниеЗначения.Количество() = 0 Тогда Продолжить; КонецЕсли; КлючЗначение = ОписаниеЗначения[0].ПолучитьГруппы(); СодержимоеГруппы.Вставить(СокрЛП(КлючЗначение[0].Значение), СокрЛП(КлючЗначение[1].Значение)); КонецЦикла; СодержимоеФайла.Вставить(ИмяГруппы, СодержимоеГруппы); Возврат СодержимоеФайла; КонецФункции
16.6. Работа с речью
16.6.1. Общая информация
При реализации современных интерфейсов может быть востребован не только визуальный интерфейс с пользователем (формы, графики, отчеты), но и интерфейс, основанный на человеческой речи или комбинированные интерфейсы (используется и графическое и речевое взаимодействие). В общем случае работу с речью можно разделить на две части: синтез речи и распознавание речи. Если использовать и распознавание речи, и синтез речи, то можно построить полноценный интерфейс с пользователем.
Данный раздел посвящен инструментам работы с речью, которые предоставляет система «1С:Предприятие».
16.6.2. Распознавание речи
16.6.2.1. Общая информация
Распознавание речи ‑ это процесс автоматического преобразования человеческой речи (как звукового сигнала) в текстовые данные. Распознавание речи может использоваться при работе с системой в различных режимах. Например, для ввода информации в текстовое поле. Другим вариантом является расшифровка записей разговоров службы поддержки для упрощения контроля качества и разбора конфликтных ситуаций.
Механизм распознавания речи в системе «1С:Предприятие» состоит из нескольких частей. Существует модель распознавания речи: это нейронная сеть, которая обучена задаче распознавания речи. У модели есть несколько наборов параметров. Один набор параметров называется грамматика. Грамматика отвечает за специфику распознавания. Другой набор параметров называется акустикой. Акустика определяет, какие внешние шумы присутствуют на фоне полезной нагрузки. Можно сказать, что акустика определяет то, каким образом будут фильтроваться фоновые шумы. Последней частью (по перечислению, но не по важности) является сама система «1С:Предприятие», которая умеет использовать выбранную модель, которая «донастроена» конкретной грамматикой. Например, в качестве модели может выступать нейросеть, обученная распознаванию разговорной речи, а настройки, необходимые для улучшенного распознавания медицинских терминов ‑ будут находиться в грамматике.
В процессе распознавания речи, механизмы платформы будут формировать результаты распознавания и вероятность этих результатов. Эта комбинация данных будет называться гипотезами. Чем более вероятна та или иная гипотеза, тем «ближе» в началу списка гипотез она будет расположена.
Распознавание речи в системе «1С:Предприятие» может выполняться в двух режимах:
● В локальном варианте ‑ в этом случае распознавание выполняется на локальном компьютере (для файлового варианта) или на компьютере (-ах) кластера серверов (для клиент-серверного варианта). Данный режим полезен в тех случаях, когда для информационной системы недоступна сеть Интернет.
● В варианте удаленного (облачного) сервиса. В этом случае собственно распознавание выполняется в облачном сервисе. В сервис отправляет записанная речь, а обратно возвращается распознанный текст.
Распознавать речь можно как в режиме «онлайн» (когда распознавание/передача в сервис выполняется сразу с микрофона компьютера, потоковое распознавание), так и в режиме «офлайн» (в этом случае для распознавания используется файл с записанной речью, отложенное распознавание).
Распознавание речи не поддерживается:
● В том случае, если информационная база создана в режиме совместимости Версия 8.1.
● В веб-клиенте, который доступен только по протоколу HTTP и не является locahost.
● В веб-клиенте, который работает под управлением:
● веб-браузера Google Chrome версии 65 и предшествующих.
● веб-браузера Microsoft Internet Explorer любых версий.
● веб-браузера Mozilla Firefox любых версий.
● веб-браузера Safari любых версий.
● веб-браузеров на основе Chromium версии 65 и предшествующих.
● В автономном сервере.
● Распознавание речи может работать некорректно в 32-разрядных системах. Модели распознавания речи и дополнительные грамматики загружаются в оперативную память и имеют существенный размер. Поэтому ограниченного объема оперативной памяти 32-разрядных систем может не хватить для загрузки.
В следующих случаях поддерживается только распознавание с помощью сервиса (не поддерживается локальное распознавание):
● На компьютерах фирмы Apple (в файловом варианте информационной базы).
● В учебной версии системы «1С:Предприятие».
● В мобильной платформе системы «1С:Предприятие».
● В мобильном клиенте системы «1С:Предприятие»:
● Не поддерживается использование распознавание речи в мобильном автономном сервере.
● Микрофон нельзя использовать совместно с другими приложениями или другой функциональностью. Попытка запустить потоковое распознавание речи при занятом микрофоне приведет к вызову исключения.
16.6.2.2. Программный интерфейс
16.6.2.2.1. Общая информация
Система «1С:Предприятие» предоставляет доступ к инструментам распознавания речи с помощью свойства глобального контекста РаботаСРечью. В документации, для упрощения, будет опускаться префикс метода РаботаСРечью.. В примерах и реальных приложениях, очевидно, такие пропуски недопустимы. Рассмотрим предоставляемые возможности более подробно.
Самая простая схема использования распознавания речи выглядит следующим образом:
● С помощью стандартной функции Управление распознаванием речи выполняется подключение к внешнему серверу распознавания.
● Указать модель, которая будет использоваться для распознавания.
● Встроить вызов потокового распознавания текста в какую-либо форму платформы.
Если эта схема чем-то не устраивает, то в данном разделе будет приведено описание методов работы с механизмом распознавания текста, который позволит вам реализовать необходимую функциональность с помощью встроенного языка самостоятельно.
Для использования механизмов распознавания речи, может потребоваться выполнить некоторые административные действия (подключить информационную базу к серверу, загрузить или выгрузить какие-то данные и т. д.). Эти действия можно выполнять как для информационной базы в целом, так и для конкретного сеанса. Действия, которые выполняются для информационной базы, требуют наличия права АдминистрированиеДанных. Действия, выполняемые для сеанса, никаких специальных прав не требуют. Если действие выполняется для информационной базы, то результатом действия смогут пользоваться все сеансы информационной базы. Если действие выполняется для сеанса, то результат действия распространяется только на текущий сеанс и после завершения сеанса результат будет утерян. В дальнейшем будут рассматриваться только методы работы с информационной базой. Методы работы с сеансом отличаются только суффиксом метода и будут рассматриваться только в том случае, когда поведение методов различается больше, чем описано ранее. Пример методов работы с информационной базой и сеансом:
● ВыполнитьРегистрациюИнформационнойБазы()/ВыполнитьРегистрациюСеанса().
● УстановитьДополнительнуюГрамматикуИнформационнойБазы()/УстановитьДополнительнуюГрамматикуСеанса().
Смотри также:
● Права доступа (см. здесь).
16.6.2.2.2. Подключение к серверу работы с речью
Для того, чтобы начать использование механизмов распознавания речи необходимо подключить модель распознавания из файла для локального распознавания или подключиться к серверу работы с речью для удаленного распознавания. Локальное подключение модели выполняется с помощью метода УстановитьМодельРаспознаванияДляИнформационнойБазы(). Модель представляет собой zip-файл, который может быть получен любым доступным образом (в том числе и путем экспорта модели из информационной базы). Результатом работы метода является загруженная в информационную базу модель и объект, описывающий эту модель (типа ОписаниеМоделиРаспознаванияРечи). Этот объект будет далее использоваться в других методах работы с распознаванием речи. Подключение модели распознавания для сеанса не поддерживается.
Если планируется использовать удаленное распознавание речи, то необходимо использовать метод ВыполнитьРегистрациюИнформационнойБазы(). В метод передаются имя пользователя и пароль на сервере работы с речью, URL которого также передается в метод с помощью параметра. Следует помнить, что при использовании метода ВыполнитьРегистрациюИнформационнойБазы(), указанные при вызове метода параметры доступа к серверу работы с речью будут использоваться всеми сеансами данной информационной базы (если не указано иного).
Если необходимо проверить, что в данный момент времени можно использовать удаленный сервер работы с речью, то следует использовать метод ПоддерживаетсяИспользованиеВнешнегоРасположения(). Метод вернет значение Истина, если ранее было выполнено подключение к внешнему серверу работы с речью.
Если требуется получить параметры внешнего подключения, то следует использовать метод ПолучитьПараметрыВнешнегоПодключенияИнформационнойБазы(). Метод вернет объект типа ПараметрыВнешнегоПодключенияРаботыСРечью, который содержит URL сервера работы с речью (свойство АдресСервера) и токен (свойство Токен), который используется для подключения к серверу. Эти свойства можно сохранить в базе данных для дальнейшего использования в методах УстановитьПараметрыВнешнегоПодключенияИнформационнойБазы(). Токен доступа к серверу работы с речью следует считать конфиденциальной информацией. Если методу УстановитьПараметрыВнешнегоПодключенияИнформационнойБазы() передать в качестве параметра значение Неопределено, то информационная база будет отключена от сервера работы с речью.
16.6.2.2.3. Распознавание речи
Собственно распознавание может выполняться двумя способами: потоковое распознавание и отложенное распознавание. В первом случае распознаваемый голос получается с устройства ввода речи клиентского компьютера (микрофона) и сразу отправляется на распознавание (сервером работы с речью или локальными механизмами платформы). Потоковое распознавание выполняется синхронно с речью пользователя. Во втором случае формируются так называемые задания распознавания: это двоичные данные с записанным голосом, которые отправляются для распознавания. Собственно распознавание выполняется асинхронно. В обеих случаях клиентское приложение получает результат распознавания речи в виде специального объекта.
Для запуска потокового распознавания необходимо использовать несколько методов. Во-первых, необходимо проверить, что текущая конфигурация системы (включая ее настройки) допускают использование потокового распознавания. Для этого предназначен метод ПоддерживаетсяПотоковоеРаспознавание(). Если метод вернул значение Истина ‑ значит можно использовать потоковое распознавание.
Во-вторых, надо запустить собственно распознавание речи с помощью метода НачатьПотоковоеРаспознавание(). В этот метод передается уникальный идентификатор распознавания (формальный параметр Ключ). Этот параметр позволяет однозначно идентифицировать каждый поток распознавания речи. С помощью формального параметра ОбработчикРаспознавания передается обработчик оповещения, который будет получать управление каждый раз, когда механизм распознавания речи формирует новые гипотезы. С помощью данного обработчика можно в любой момент прекратить потоковое распознавание. Параметры распознавания речи передаются через формальные параметры ПараметрыМодели и ПараметрыПотоковогоРаспознавания и будут рассмотрены далее. Еще одним параметр (формальный параметр ВариантИспользованияРасположенияРаботыСРечью) позволяет указать, какой механизм распознавания будет использоваться: локальный, удаленный или система выберет используемый механизм автоматически. После того, как все нужные данные введены и распознаны, можно завершить потоковое распознавание или с помощью возвращаемого параметра обработчика оповещения (параметр ПродолжитьРаспознавание) или с помощью метода ОстановитьПотоковоеРаспознавание(). Методу ОстановитьПотоковоеРаспознавание() необходимо указать ключ того потока распознавания, который необходимо завершить.
Копировать в буфер обмена&НаКлиенте Процедура ЗапуститьПотоковоеРаспознавание(Команда) КлючРаспознавания = Строка(Новый УникальныйИдентификатор); Обработчик = Новый ОписаниеОповещения("ОбработчикРаспознавания", ЭтотОбъект); РаботаСРечью.НачатьПотоковоеРаспознавание(КлючРаспознавания, Обработчик); КонецПроцедуры &НаКлиенте Процедура ОбработчикРаспознавания(РезультатРаспознавания, ПродолжитьРаспознавание, ДополнительныеПараметры) Экспорт Сообщить("Фраза распознана - " + РезультатРаспознавания.РаспознаваниеФразыЗавершено); Для каждого ДанныеФразы Из РезультатРаспознавания.ДанныеФраз Цикл РаспознанныйТекст = РаспознанныйТекст + ДанныеФразы.Фраза + Символы.ПС; КонецЦикла; КонецПроцедуры
В данном примере РаспознанныйТекст ‑ это реквизит формы типа Строка.
Результат распознавания речи попадает в обработчик оповещения в виде значения типа РезультатРаспознаванияРечи. Данный объект содержит следующую информацию:
● Свойство ДанныеФраз содержит массив объектов ДанныеФразыРаспознаванияРечи. Фактически это текстовое представление речи. Элементы массива упорядочены по убыванию значения свойства Уверенность каждого элемента массива. Каждый объект ДанныеФразыРаспознаванияРечи описывается следующими свойствами:
● Фраза ‑ свойство содержит текстовое представление гипотезы.
● Уверенность ‑ число, описывающее то, насколько содержимое свойства Фраза соответствует реально произнесенным словам. Чем больше значение, тем выше соответствие (выше уверенность).
● ВремяНачала ‑ смещение начала фразы (в миллисекундах) от начала распознавания.
● ВремяКонца ‑ смещение окончания фразы (в миллисекундах) от начала распознавания.
● СловаФраз ‑ массив объектов СловоФразыРаспознаванияРечи, который содержит информацию о каждом слове распознанной фразы. Каждый объект описывается следующими свойствами:
● Слово ‑ содержит текстовое представление слова гипотезы.
● Уверенность ‑ число, описывающее то, насколько содержимое свойства Слово соответствует реально произнесенному слову. Чем больше значение, тем выше соответствие (выше уверенность).
● ВремяНачала ‑ смещение начала слова (в миллисекундах) от начала распознавания.
● ВремяКонца ‑ смещение окончания слова (в миллисекундах) от начала распознавания.
● Свойство РаспознаваниеФразыЗавершено будет равно значению Истина, если механизм распознавания речи считает, что фраза закончена и гипотезы, переданные в свойстве ДанныеФраз являются окончательными относительно сказанной фразы.
Теперь рассмотрим, как задается используемая модель распознавания и параметры потокового распознавания. Система работы с речью не позволяет указать конкретную модель, которая будет использоваться при распознавании. Разработчик может указать «пожелания», которым должна соответствовать модель распознавания, и система автоматически подберет наиболее подходящую модель (из списка имеющихся). Если указать в качестве значения формального параметра ПараметрыМодели результат работы метода ПолучитьПараметры() описания модели распознавания речи, то даже в этом случае не гарантируется, что будет выбрана в точности эта модель. Если разработчик не имеет каких-либо «пожеланий» к модели, то параметр ПараметрыМодели можно не указывать. Система автоматически выберет наиболее подходящую модель.
В том случае, если у разработчика есть какие-либо «пожелания» по выбору модели, то следует создать объект типа ПараметрыМоделиРаспознаванияРечи и указать «пожелания» для свойств этого объекта. Список возможных значений для формирования отбора можно получить из описания доступных моделей в конкретной системе. С точки зрения общего описания работы отметим, что язык распознавания (свойство КодЯзыка) должен быть указан всегда, а другие свойства объекта используются следующим образом:
● Если у объекта ПараметрыМоделиРаспознаванияРечи какое-либо свойство указано явным образом, тогда система «1С:Предприятие» будет искать модель, в точности соответствующей этому значению.
● Если свойство установлено в значение Неопределено, то система «1С:Предприятие» будет автоматически подбирать наиболее подходящую модель распознавания (из имеющихся в доступе).
Параметры потокового распознавания речи задаются с помощью объекта типа ПараметрыПотоковогоРаспознаванияРечи. Этот объект позволяет регулировать следующие параметры:
|
Свойство |
Описание |
|
ДополнительныеГрамматики |
Тип: Массив. Параметр позволяет указать список специфических слов, которые с высокой вероятностью будут присутствовать в распознаваемом разговоре. Может повысить качество распознавания на специфических текстах. |
|
ИспользоватьТолькоДополнительныеГрамматики |
Тип: Булево. Если указано Истина, то дополнительные грамматики будут выступать фильтром для распознавания. В результаты распознавания будут попадать только слова из дополнительных грамматик. |
|
ОбработчикОстановкиАудиозаписи |
Тип: ОписаниеОповещения. В данном свойстве можно указать обработчик оповещения, который будет вызываться для сохранения аудиозаписи, которая обработана во время потокового распознавания. |
|
ПолучатьВариантыПромежуточныхГипотез |
Тип: Булево. Указывает необходимость получения вариантов промежуточных гипотез (значение Истина). В этом случае в список гипотез будут размещаться не только основные гипотезы, но и все промежуточные гипотезы, в достоверности которых алгоритм распознавания «не уверен». |
|
ПолучатьПромежуточныеРезультаты |
Тип: Булево. Указывает необходимость получения промежуточных гипотез во время распознавания (значение Истина). |
Если стандартные настройки потокового распознавания устраивают, то явное указание параметра ПараметрыПотоковогоРаспознавания метода НачатьПотоковоеРаспознавание() не требуется.
Отложенное распознавание речи можно использовать в том случае, когда необходимо распознать речь, которая имеется в виде звукового файла. Например, это запись разговора линии консультации. Платформа «1С:Предприятие» предоставляет набор методов, предназначенных для работы с отложенным распознаванием речи. Отложенное распознавание запускается с помощью метода ВыполнитьОтложенноеРаспознавание(). Формальный параметр Данные служит для передачи распознаваемых данных в виде двоичных данных. После того, как данные переданы на сервер работы с речью, метод завершит свою работу, предоставив вызывающей системе значение типа ИдентификаторОтложенногоРаспознаванияРечи. С помощью этого значения и метода ПолучитьРезультатыОтложенныхРаспознаваний() прикладной код может в дальнейшем получать результаты распознавания одного (или нескольких) отложенных распознаваний речи. В качестве результата работы метода будет получен массив такой же размерности, что и передан в метод. В качестве элементов массива будут выступать значения типа РезультатОтложенногоРаспознаванияРечи. Если в процессе распознавания стало понятно, что это отложенное распознавание больше не нужно, то прекратить отложенное распознавание можно с помощью метода ОтменитьОтложенноеРаспознавание(). В метод передается соответствующий идентификатор отложенного распознавания.
Результат отложенного распознавания речи несколько отличается от результата потокового распознавания речи. Если результата потокового распознавания речи содержит только гипотезы и признак распознавания фразы, то результат отложенного распознавания (объект типа РезультатОтложенногоРаспознаванияРечи) содержит некоторую дополнительную информацию:
|
Свойство |
Описание |
|
Завершено |
Свойство содержит значение Истина, если данное отложенное распознавание завершено. Значение Ложь означает, что распознавание еще выполняется. |
|
Ключ |
Идентификатор отложенного распознавания речи. |
|
Ошибка |
Содержит строку сообщения об ошибке, если распознавание речи завершено аварийно или отменено. |
|
Результаты |
В данном свойстве расположены результаты распознавания, которые полностью аналогичны данным, которые попадают в обработчик оповещения при выполнении потокового распознавания речи. |
|
Успешно |
Свойство содержит значение Истина, если данное распознавание завершилось успешно. Значение Ложь означает, что распознавание отменено. |
16.6.2.2.4. Модели распознавания и работа с ними
Ранее мы рассмотрели, что модель распознавания речи можно подключить с помощью метода УстановитьМодельРаспознаванияДляИнформационнойБазы(). Рассмотрим вопрос работы с моделями и грамматиками более подробно.
Для того, чтобы установить модель, необходимо иметь устанавливаемую модель в виде специального файла. Его структура не является предметом документирования, но важно знать, что это zip-архив. Получить такой файл можно с помощью метода ПолучитьМодельРаспознаванияВоВРеменноеХранилище(). Для того, чтобы узнать идентификатор модели (который необходим для использования метода), следует воспользоваться методом ПолучитьОписаниеМоделейРаспознавания(). В качестве возвращаемого значения будет получен массив объектов ОписаниеМоделиРаспознаванияРечи. Объект содержит следующие свойства:
|
Свойство |
Описание |
|
Версия |
Текущая версия модели. |
|
ДоступнаВЛокальномВарианте |
Равно значению Истина, если данная модель доступна в локальном варианте. |
|
ДоступнаВоВнешнемВарианте |
Равно значению Истина, если данная модель доступна на сервере работы с речью. |
|
ДоступнаДляПолучения |
Если модель доступна для скачивания, то в данном свойстве находится значение Истина. |
|
ИдентификаторМодели |
Идентификатор модели (тип ИдентификаторМоделиРаспознаванияРечи). |
|
Описание |
Человекочитаемое описание модели распознавания речи. |
|
ПредставлениеАкустики |
Человекочитаемое описание акустики, используемой в модели. |
|
ПредставлениеГрамматики |
Человекочитаемое описание грамматики, используемой в модели. |
Из описания модели можно получить параметры модели распознавания речи с помощью метода ОписаниеМоделиРаспознаванияРечи.ПолучитьПараметры(). Эти параметры можно использовать в тех методах, которые требуют описание модели распознавания речи в качестве одного их своих параметров.
Копировать в буфер обмена&НаСервере Функция СохранитьМодельНаСервере() СписокМоделей = РаботаСРечью.ПолучитьОписаниеМоделейРаспознавания(); Возврат РаботаСРечью.ПолучитьМодельРаспознаванияВоВременноеХранилище(СписокМоделей[0].ИдентификаторМодели); КонецФункции &НаКлиенте Асинх Процедура СохранитьМодель(Команда) СсылкаНаМодель = СохранитьМодельНаСервере(); Ждать ПолучитьФайлССервераАсинх(СсылкаНаМодель, "", Новый ПараметрыДиалогаПолученияФайлов); КонецПроцедуры
Следует отметить, что список описаний моделей (который возвращает метод ПолучитьОписаниеМоделейРаспознаванияРечи()) возвращает список как локальных моделей, так и моделей, которые доступны на сервере работы с речью. Список моделей зависит от того, есть подключение к серверу работы с речью или нет. Как видно из описания модели, свойства описания позволяют узнать, где находится модель (на локальном компьютере или на сервере работы с речью) и можно-ли ее загрузить для локального использования. Получение моделей распознавания речи возможно с использованием отбора, в котором мы можем указать, какие модели нам нужны. Например, можно указать, что нам нужны только модели распознавания русского языка.
Если модель распознавания более не нужна в информационной базе, то ее можно удалить с помощью метода УдалитьМодельРаспознаванияДляИнформационнойБазы(). Для сеанса данная возможность не поддерживается (как и установка модели).
Смотри также:
● Управление размещением хранилища моделей (см. здесь).
16.6.2.2.5. Дополнительные грамматики распознавания речи
Дополнительная грамматика ‑ это словарь, который увеличивает вероятность появления слов из этого словаря в результатах распознавания текста. Другими словами, дополнительная грамматика позволяет сместить фокус распознавания в сторону набор слов, заданных в этой грамматике. Например, если дополнительная грамматика будет содержать терминологию, характерную для торговли запасными частями к автомобилям, то универсальная модель распознавания речи сможет более уверенно понимать разговоры, имеющие такую специфику.
Дополнительная грамматика создается прикладным разработчиком (или пользователем) и содержит простой набор слов, которые должна понимать модель. Дополнительная грамматика может быть установлена как для информационной базы, так и для сеанса.
Установка дополнительной грамматики выполняется с помощью метода УстановитьДополнительнуюГрамматикуИнформационнойБазы(). Идентификатор грамматики создается прикладным разработчиком. Этот идентификатор предназначен исключительно для работы с дополнительными грамматиками из встроенного языка.
Список подключенных дополнительных грамматик можно получить с помощью метода ПолучитьДополнительныеГрамматикиИнформационнойБазы(), который возвращает массив ключей установленных грамматик. Зная ключ, можно удалить дополнительную грамматику с помощью метода УдалитьДополнительнуюГрамматикуИнформационнойБазы().
Методов выгрузки дополнительных грамматик не существует, т. к. набор слов дополнительных грамматик подготавливается пользователями информационной базы и сохранность этого набора слов лежит на самих пользователях.
16.6.2.2.6. Перенос данных распознавания речи в другую информационную базу
Для переноса параметров работы с распознаванием речи между информационными базами необходимо выгрузить эти данные в базе-источнике и загрузить в базе-приемнике. Основные параметры могут быть получены с помощью метода ПолучитьДанныеРаботыСРечьюИнформационнойБазы(). Данный метод вернет объект типа ДанныеИнформационнойБазыРаботыСРечью, который не имеет свойств и методов, но может быть сериализован в XDTO. Т. к. данный объект не содержит локальные модели распознавания речи, то эти модели необходимо дополнительно выгрузить в файлы с помощью метода ПолучитьМодельРаспознаванияВоВременноеХранилище() и затем записать временное хранилище в файл.
Для загрузки данных необходимо использовать метод УстановитьДанныеРаботыСРечьюИнформационнойБазы(). В качестве параметра должен выступать десериализованный объект ДанныеИнформационнойБазыРаботыСРечью, полученный из базы-источника данных. Затем можно загрузить модели распознавания, которые были выгружены из базы-источника, с помощью метода УстановитьМодельРаспознаванияДляИнформационнойБазы().
16.6.3. Синтез речи
16.6.3.1. Общая информация
Синтез речи ‑ это формирование звукового сигнала по текстовому представлению некоторого текста. Понятно, что синтез речи может выполняться различными голосами, каждый из которых может обладать своим, уникальным, набором параметров.
Синтез речи может выполняться следующим образом:
● Потоковый синтез ‑ в этом случае звуковой сигнал, полученный в результате синтеза, сразу отправляется на устройство воспроизведения по умолчанию того компьютера, на котором запущен синтез речи.
● Синтез в файл ‑ в этом случае синтезированная речь записывается в файл. Куда и каким образом будет воспроизведен созданный файл ‑ лежит за границей инструмента.
В любом случае, синтез речи выполняется удаленным (облачным) сервером. Синтез речи локально (на клиенте или сервере) не поддерживается.
Синтез речи не поддерживается:
● На локальном компьютере или мобильном устройстве (с использованием мобильной версии), без подключения к Интернет и доступа к удаленному сервису.
16.6.3.2. Программный интерфейс
Система «1С:Предприятие» предоставляет доступ к инструментам синтеза речи с помощью свойства глобального контекста РаботаСРечью. В документации, для упрощения, будет опускаться префикс метода РаботаСРечью.. В примерах и реальных приложениях, очевидно, такие пропуски недопустимы. Рассмотрим предоставляемые возможности более подробно.
Для того, чтобы использовать синтез речи, необходимо выбрать, какой голос будет использоваться для синтеза и указать его (голоса) параметры. После этого становится возможно осуществлять синтез речи.
Для получения доступны голосов следует использовать метод ПолучитьДоступныеГолосаСинтеза(). В качестве результата работы метод вернет массив объектов ОписаниеГолосаСинтезаРечи. Этот объект содержит следующие свойства:
|
Свойство |
Описание |
|
Голос |
Идентификатор голоса. |
|
КодЯзыка |
На каком языке будет выполняться синтез речи. |
|
Описание |
Описание голоса на языке текущего сеанса. |
|
Параметры |
Фиксированный массив, который содержит параметры голоса. Каждый параметр представлен объектом типа ОписаниеПараметраГолосаСинтезаРечи. |
|
Представление |
Идентификатор голоса на языке текущего сеанса. |
Каждый параметр голоса представлен объектом ОписаниеПараметраГолосаСинтезаРечи со следующими свойствами:
|
Свойство |
Описание |
|
ДопустимыеЗначения |
Фиксированный массив, содержащий допустимые значения параметра. Каждый параметр представлен объектом типа ОписаниеЗначенияПараметраГолосаСинтезаРечи. |
|
Описание |
Описание параметра голоса на языке текущего сеанса. |
|
Параметр |
Ключ (идентификатор) параметра голоса. |
|
Представление |
Ключ параметр голоса на языке текущего сеанса. |
Каждый значение параметр голоса представлено объектом ОписаниеЗначенияПараметраГолосаСинтезаРечи со следующими свойствами:
|
Свойство |
Описание |
|
Значение |
Допустимое значение параметра голоса синтеза речи. |
|
Описание |
Описание значения параметра голоса на языке текущего сеанса. |
|
Представление |
Ключ параметр голоса на языке текущего сеанса. |
Для того, чтобы получить доступные голоса синтеза, необходимо быть подключенным к серверу работы с речью.
Дальнейшая работа по синтезу речи выглядит достаточно просто, но следует помнить, что следующий пример работает на стороне клиентского приложения:
Копировать в буфер обмена&НаКлиенте Процедура НачатьГенерацию(Текст, Голос, Параметры) ДопПараметры = Новый Структура; ДопПараметры.Вставить("Ключ", Строка(Новый УникальныйИдентификатор)); ОписаниеОповещения = Новый ОписаниеОповещения("ОбработчикОкончанияСинтеза", ЭтаФорма, ДопПараметры, , ЭтаФорма); РаботаСРечью.НачатьСинтез(Текст, ДопПараметры.Ключ, Голос, Параметры, ОписаниеОповещения); КонецПроцедуры &НаКлиенте Асинх Процедура ОбработчикОкончанияСинтеза(РезультатСинтеза, ДополнительныеПараметры) Экспорт РаботаСРечью.ОстановитьСинтез(ДополнительныеПараметры.Ключ); КонецПроцедуры
В данном примере:
● Текст ‑ формальный параметр, который содержит текст, требующий «произнесения».
● Голос ‑ объект типа ОписаниеГолосаСинтезаРечи. Это одно из значений, которые вернул метод ПолучитьДоступныеГолосаСинтеза().
● Параметры ‑ соответствие, в которое помещены параметры голоса синтеза речи.
● Когда синтез речи будет завершен, управление попадет в обработчик оповещения ОбработчикОкончанияСинтеза.
Потоковый синтез можно остановить в любой момент. Для этого необходимо знать только ключ останавливаемого процесса синтеза речи (который задается при старте синтеза). Этот ключ должен быть уникальным в рамках сервера работы с речью и должен позволять однозначно идентифицировать тот или иной поток синтеза речи. Остановить процесс синтеза можно с помощью метода ОстановитьСинтез().
Обработчик окончания синтеза речи в параметре РезультатСинтеза получает WAV-файл, который содержит результат синтеза. Если необходимо, то этот результат можно сохранить на дисковый накопитель.
В том случае, когда воспроизведение синтезированной речи и момент собственно синтеза разнесены между собой (например, синтез речи осуществляется на стороне сервера), то следует использовать метод Синтезировать(). Этот метод принимает на вход те же основные параметры, что и его клиентский аналог: текст, голос и параметры голоса. В качестве результата возвращается значение типа ДвоичныеДанные, которые представляют собой файл с синтезированным текстом.
16.7. Работа с архивами
16.7.1. Общая информация
В работе систем автоматизации встречаются ситуации, когда необходимо работать с архивами. Архив ‑ это файл-контейнер, который содержит в себе один или несколько файлов или папок, а также различные метаданные. Файлы, которые содержатся в архиве, могут быть дополнительно сжаты для уменьшения занимаемого места. Архивы используются с разными целями, но чаще всего можно выделить следующие: перенос информации, хранение информации, сжатие данных. Система «1С:Предприятие» поддерживает следующие форматы архивов:
● Для извлечения из архива: BZIP2, GZIP, RAR, 7-ZIP, TAR, XZ, ZIP.
● Для помещения в архив: ZIP.
Говоря об архивах, можно выделить следующие основные операции:
● Создание архива.
● Добавление файлов в архив.
● Извлечение файлов из архива.
В данном разделе будет описано, какие объекты предоставляет система «1С:Предприятие» для работы с архивами и для выполнения основных операций.
16.7.2. Запись архива
Для записи архива предназначен объект ЗаписьФайлаАрхива. Данный объект создается на основании собственно файла, в котором будет размещен архив, на основании потока, в который будет записываться архив или в неинициализированном виде. Если объект создается в неинициализированном виде, то перед тем, как начать добавление файлов в архив, объект надо инициализировать вызовом метода Открыть() или использовать особый режим получения архива.
Конструкторы на основании файла или потока, а также метод Открыть() имеют практически одинаковый набор параметров, которые и будут сейчас рассмотрены. Первым параметром выступает объект, который будет содержать архив: имя файла или один из видов потоков. Затем можно указать Пароль, которым будет защищен создаваемый архив. Параметр ТипФайлаАрхива может содержать только значение системного перечисления ТипФайлаАрхива.ZIP. Параметр Комментарий позволяет задать текстовое описание создаваемого архива. Параметр МетодСжатия определяет то, с помощью какого алгоритма будет выполняться сжатие файлов при добавлении в архив. Алгоритм сжатия определяется значением системного перечисления МетодСжатияФайлаАрхива:
● BZIP2 ‑ предписывает использование алгоритма BZIP. Более высокая степень сжатия, но больше время архивации.
● Сжатие ‑ предписывает использовать алгоритм Deflate. Меньше степень сжатия, но выше скорость архивации. Значение по умолчанию.
● Копирование ‑ в этом случае файлы при добавлении в архив не будут сжиматься, а будут добавляться «как есть».
Параметр УровеньСжатия определяет, как полно будет использовать тот или иной алгоритм. Значение параметра описывается системным перечислением УровеньСжатияФайлаАрхива:
● Максимальный ‑ в этом случае будут использованы все возможности алгоритма сжатия, в результате файл будет сжат максимальным образом. На архивацию будет затрачено максимальное время.
● Минимальный ‑ возможности алгоритма используются в минимальном объеме, в результате файл будет сжат минимально. Время архивации также минимально.
● Оптимальный ‑ данный вариант уровня сжатия представляет разумный баланс между скоростью работы алгоритма и результирующим размером файла. Значение по умолчанию.
Если при формировании файла архива указан пароль, то параметр МетодШифрования определяет, какой алгоритм будет использоваться при шифровании архива. Метод шифрования определяет значение системного перечисления МетодШифрованияФайлаАрхива:
● AES128 ‑ для шифрования используется алгоритм AES с длиной блока в 128 бит.
● AES192 ‑ для шифрования используется алгоритм AES с длиной блока в 192 бита.
● AES256 ‑ для шифрования используется алгоритм AES с длиной блока в 256 бит.
● Zip20 ‑ для шифрования используется алгоритм Zip версии 2.0 (длина блока 96 бит). При таком шифровании будет достигнута совместимость с другими архиваторами в формат ZIP. Другие алгоритмы шифрования не гарантируют совместимости (и возможности распаковать такой архив сторонними средствами). Значение по умолчанию.
Наконец кодировку имен файлов в архиве определяет параметр Кодировка, значения которого задаются системным перечислением КодировкаИменФайловВФайлеАрхива:
● UTF8 ‑ в этом случае имена файлов в архиве будут находиться в кодировке UTF-8.
● КодировкаОСДополнительноUTF8 ‑ в этом случае имена файлов в архиве будут находится в двух кодировка: UTF-8 и текущая кодировка операционной системы.
● Авто ‑ кодировка имен файлов определяется на основании параметра FileNamesEncodingInZipFile конфигурационного файла conf.cfg. Если данный параметр не указан в конфигурационном файле, то используется значение UTF8. Значение по умолчанию.
После того, как создан объект типа ЗаписьФайлаАрхива, можно добавлять в архив нужные файлы. Для этого предназначен метод объекта Добавить(). Метод позволяет добавить в архив один или несколько файлов. Если первый параметр метода (с именем ИмяФайла) содержит строку, которая однозначно определяет только один файл ‑ метод добавит в архив один файл. Если в качестве значения параметра задана маска, то метод добавит все файлы, который доступны пользователю, от имени которого исполняется «1С:Предприятие» и которые соответствуют этой маске. Параметр МетодСохраненияПутей (типа системного перечисления РежимСохраненияПутейФайлаАрхива) описывает, каким образом в архиве будет сохранен полный путь добавляемого файла:
● НеСохранятьПути ‑ в этом случае в архиве будет утеряно исходное местоположение файла в иерархии файловой системы. Значение по умолчанию.
● СохранятьОтносительныеПути ‑ в этом случае сохраняются относительные пути размещения каждого файла. Пути сохраняются относительно текущего каталога, т. е. каталога, в котором формируется файл архива.
● СохранятьПолныеПути ‑ для каждого файла будет сохранен полный путь (считая от корневого элемента файловой системы используемой операционной системы), по которому файл был расположен в момент добавления в архив.
Параметр РекурсивнаяОбработкаПодпапок определяет, каким образом будут обрабатываться подкаталоги, расположенные в текущем каталоге. Этот параметр имеет смысл указывать в том случае. если добавляется не один, а несколько файлов (для добавляемых файлов указана маска). Параметр принимает значение системного перечисления РежимОбработкиПодкаталоговФайлаАрхива:
● НеОбрабатывать ‑ в этом случае файлы добавляются только из текущего каталога. Обход вложенных каталогов не выполняется. Значение по умолчанию.
● ОбрабатыватьРекурсивно ‑ в этом случае система обходит каждый вложенный каталог на предмет поиска файлов, подходящих по маске добавляемых файлов. Обход происходит на полную глубину. Глубину обхода ограничить невозможно.
После того, как все файлы добавлены, архив необходимо закрыть и записать на диск. Для этого предназначен метод объекта Записать(). Архив закрывается (в него больше нельзя добавлять файлы) и записывается на диск. Если файл с таким именем существует ‑ он перезаписывается.
Если объект ЗаписьФайлаАрхива создавался неинициализированным, то можно добавлять в него файлы, но вместо метода объекта Записать() использовать метод объекта ПолучитьДвоичныеДанные(). Параметры метода полностью аналогичны параметрам конструктора или метода Открыть(). Но результатом выполнения метода будет объект типа ДвоичныеДанные. Которые можно подвергнуть дальнейшей обработке или передаче в качестве параметра какого-либо Интернет-сервиса.
В простейшем случае, добавление всех файлов (по определенной маске) из каталога в архив, будет выглядеть следующим образом:
Копировать в буфер обмена&НаКлиенте Процедура ДобавитьФайлыВАрхив(ИмяАрхива, ДобавляемыеФайлы) Архив = Новый ЗаписьФайлаАрхива(ИмяАрхива, "", ТипФайлаАрхива.ZIP, "", МетодСжатияФайлаАрхива.BZIP2, УровеньСжатияФайлаАрхива.Максимальный); Архив.Добавить(ДобавляемыеФайлы, РежимСохраненияПутейФайлаАрхива.СохранятьОтносительныеПути, РежимОбработкиПодкаталоговФайлаАрхива.ОбрабатыватьРекурсивно); Архив.Записать(); КонецПроцедуры
Аналогичные действия, только через объект типа ДвоичныеДанные (который является возвращаемым значением функции), выглядят следующим образом:
Копировать в буфер обмена&НаКлиенте Функция ДобавитьФайлыВАрхивДвоичныеДанные(ДобавляемыеФайлы) Архив = Новый ЗаписьФайлаАрхива; Архив.Добавить(ДобавляемыеФайлы, РежимСохраненияПутейФайлаАрхива.СохранятьОтносительныеПути, РежимОбработкиПодкаталоговФайлаАрхива.ОбрабатыватьРекурсивно); ДвоичныеДанные = Архив.ПолучитьДвоичныеДанные("", ТипФайлаАрхива.ZIP, "", МетодСжатияФайлаАрхива.BZIP2, УровеньСжатияФайлаАрхива.Максимальный); Возврат ДвоичныеДанные; КонецФункции
Смотри также:
● Работа с двоичными данными (см. здесь).
● Параметр FileNamesEncodingInZipFile конфигурационного файла cong.cfg (см. здесь).
16.7.3. Чтение архива
Чтение архива является процедурой, обратной записи архива. Выполняется эта операция с помощью объекта ЧтениеФайлаАрхива. Система «1С:Предприятие» позволяет читать архивы различных форматов. Поддерживаемые форматы описываются системным перечислением ТипФайлаАрхива. Общий процесс чтения архива выглядит следующим образом:
● Открывается файл архива.
● Анализируется содержимое архива (по именам файлов), извлекаются один или несколько файлов.
● Закрывается файл с архивом.
Рассмотрим подробнее данные операции. Для того, чтобы прочитать файл архива, следует создать объект ЧтениеФайлаАрхива. Файл архива может содержаться в файле или потоке. При открытии файла надо определить, какой архива открывается. Система может сама определить этот тип по расширения файла архива, а если расширение не указывает на тип, то можно указать тип с помощью специального параметра.
Все способы открытия файла архива (конструкторы или метод Открыть()) имеют одинаковый набор параметров:
● Первый параметр определяет источник архива. Если открывается файл, то его имя передается значением типа Строка. Если работа выполняется с потоками, то типом первого параметра должно быть один из следующих типов: Поток, ПотокВПамяти, ФайловыйПоток.
● Пароль ‑ значение типа Строка. Содержит пароль, с помощью которого создавался архив. Если пароль не указывался при создании ‑ нет необходимости указывать его и при открытии. Если архив с паролем попытаться открыть без указания пароля ‑ будет ошибка и открыть архив не получится. Как результат ‑ для архивов с паролем значение данного параметра обязательно и должно соответствовать пароля, использованному при создании архива.
● ТипФайлаАрхива ‑ значение типа ТипФайлаАрхива. Как было сказано выше, с помощью данного параметра можно указать типа открываемого архива. Если параметр не указан ‑ тип файла будет определен исходя из расширения. Если определить тип однозначно не получится ‑ будет сформирована ошибка.
Если архив открыт успешно, то у объекта ЧтениеФайлаАрхива есть несколько свойств, описывающих этот архив:
|
Свойство |
Описание |
|
Комментарий |
Данное свойство содержит комментарий архива (если комментарий был указан при создании). |
|
Элементы |
Данное свойство содержит значение типа ЭлементыФайлаАрхива. Данная коллекция содержит файлы, которые помещены в архив при создании. Каждый файл описывается значением типа ЭлементФайлаАрхива. |
Каждый файла, расположенный в архиве (тип значения ЭлементФайлаАрхива) имеет следующие свойства:
|
Свойство |
Описание |
|
ВремяИзменения |
Время последнего изменения файла перед помещением в архив. Тип: Дата. |
|
Зашифрован |
Признак того, что файл зашифрован. В рамках одного архива могут находиться файлы, которые зашифрованы разными паролями. Интерфейс, которые предоставляет платформа «1С:Предприятие» для работы с zip-архивами, не поддерживает такую возможность, но некоторые форматы архивов позволяют так сделать. Тип: Булево. |
|
Имя |
Содержит имя файла вместе с расширением. Тип: Строка. |
|
ИмяБезРасширения |
Содержит имя файла без расширения. Тип: Строка. |
|
ИсходноеИмя |
Свойства, которые начинаются с префикса Исходное, содержат соответствующие части имени файла так, как эта часть файла была задана в той операционной системе, в которой формировался файл архива. |
|
ИсходноеИмяБезрасширения |
|
|
ИсходноеПолноеИмя |
|
|
ИсходноеРасширение |
|
|
ИсходныйПуть |
|
|
Невидимый |
Содержит свойство «Невидимый»/«Hidden» из операционной системы, которая использовалась для создания архива. Тип: Булево. |
|
ПолноеИмя |
Содержит полное имя файла: путь (включая накопитель, если он применим), имя файла и расширение имени файла. Тип: Строка. |
|
Путь |
Содержит путь файла в архиве. На это свойство влияет параметр РежимСохраненияПутейФайлаАрхива при создании архива. Тип: Строка. |
|
РазмерНесжатого |
Содержит размер файла в байтах, который файл занимал на исходном накопителе до помещения в архив. Тип: Строка. |
|
РазмерСжатого |
Содержит размер файла в байтах, который файл занимает в файле архиве. Тип: Строка. |
|
Расширение |
Содержит расширение файла. Тип: Строка. |
|
ТолькоЧтение |
Содержит свойство «Только для чтения»/«Read only» из операционной системы, которая использовалась для создания архива. Тип: Булево. |
Свойства, которые начинаются с префикса Исходное, как уже было сказано, содержат соответствующие части имени файла архива, которые были до помещения файла в архив, в исходной операционной системе:
● Разделители частей пути остаются в том виде, как они были в исходной операционной системе. Обычные свойства (без префикса) содержат разделители пути, которые соответствуют операционной системе, на которой в данный момент работает система «1С:Предприятие».
● Некоторые специальные символы (перечислены в синтакс-помощнике), которые могут оказаться в полном имени файла, в обычных свойствах заменяются на символ «_», но сохраняются в свойствах, описывающих исходное состояние файла.
● Если в архиве обнаружены повторяющиеся имена файлов на одном уровне вложенности каталогов, то имена файлов будут модифицированы для устранения дубликатов, а исходные имена будут содержать исходные имена файлов.
Если требуется извлечь содержимое архива в какое-либо место файловой системы (например, во временный каталог), то следует использовать метод ИзвлечьВсе() объекта ЧтениеФайлаАрхива. Простейший вариант распаковки какого-либо архива с сохранением файловой иерархии, будет выглядеть следующим образом:
Копировать в буфер обмена&НаКлиенте Асинх Процедура РаспаковатьВоВременныйКаталог(ИсходныйАрхив) ВременныйКаталог = ПолучитьИмяВременногоФайла(".arch"); Ждать СоздатьКаталогАсинх(ВременныйКаталог); Архив = Новый ЧтениеФайлаАрхива(ИсходныйАрхив); Архив.ИзвлечьВсе(ВременныйКаталог, РежимВосстановленияПутейФайлаАрхива.Восстанавливать); КонецПроцедуры
Если извлекать все файлы не требуется, то можно использовать метод Извлечь(), который применим для одно файла. Следует обратить внимание на следующую особенность: если метод ИзвлечьВсе() предполагает, что у архива установлен единственный пароль (который указывается при открытии архива), то метод Извлечь() позволяет указать индивидуальный пароль на каждый файл архива. Для примера рассмотрим извлечение из архива двух файлов, каждый из которых добавлен со индивидуальным паролем: для файла 1.mxl требуется пароль 123, а для файла 2.mxl требуется пароль 345. Все остальное содержимое архива нас не будет интересовать.
Копировать в буфер обмена&НаКлиенте Асинх Процедура ИзвлечьФайлыСПаролем(ИмяФайлаАрхива) ФайлыПароли = Новый Соответствие; ФайлыПароли.Вставить("1.mxl", "123"); ФайлыПароли.Вставить("2.mxl", "345"); Архив = Новый ЧтениеФайлаАрхива(ИмяФайлаАрхива); ФайлАрхива = Новый Файл(ИмяФайлаАрхива); Для каждого ФайлПароль Из ФайлыПароли Цикл Файл = Архив.Элементы.Найти(ФайлПароль.Ключ); Если Файл <> Неопределено Тогда Архив.Извлечь(Файл, ФайлАрхива.Путь, РежимВосстановленияПутейФайлаАрхива.НеВосстанавливать, ФайлПароль.Значение); КонецЕсли; КонецЦикла; КонецПроцедуры